mempool/chain: move some methods around.
This commit is contained in:
parent
2715e71ae8
commit
d4c2331a11
@ -973,14 +973,15 @@ ChainDB.prototype.getFullBlock = co(function* getFullBlock(hash) {
|
|||||||
/**
|
/**
|
||||||
* Fill a transaction with coins (only unspents).
|
* Fill a transaction with coins (only unspents).
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @returns {Promise} - Returns {@link TX}.
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ChainDB.prototype.fillCoins = co(function* fillCoins(tx) {
|
ChainDB.prototype.fillCoins = co(function* fillCoins(tx) {
|
||||||
|
var found = true;
|
||||||
var i, input, prevout, coin;
|
var i, input, prevout, coin;
|
||||||
|
|
||||||
if (tx.isCoinbase())
|
if (tx.isCoinbase())
|
||||||
return;
|
return found;
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
@ -991,27 +992,33 @@ ChainDB.prototype.fillCoins = co(function* fillCoins(tx) {
|
|||||||
|
|
||||||
coin = yield this.getCoin(prevout.hash, prevout.index);
|
coin = yield this.getCoin(prevout.hash, prevout.index);
|
||||||
|
|
||||||
if (!coin)
|
if (!coin) {
|
||||||
|
input.coin = null;
|
||||||
|
found = false;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
input.coin = coin;
|
input.coin = coin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill a transaction with coins (all historical coins).
|
* Fill a transaction with coins (all historical coins).
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @returns {Promise} - Returns {@link TX}.
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ChainDB.prototype.fillHistory = co(function* fillHistory(tx) {
|
ChainDB.prototype.fillHistory = co(function* fillHistory(tx) {
|
||||||
|
var found = true;
|
||||||
var i, input, prevout, prev;
|
var i, input, prevout, prev;
|
||||||
|
|
||||||
if (!this.options.indexTX)
|
if (!this.options.indexTX)
|
||||||
return;
|
return found;
|
||||||
|
|
||||||
if (tx.isCoinbase())
|
if (tx.isCoinbase())
|
||||||
return;
|
return found;
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
@ -1022,14 +1029,16 @@ ChainDB.prototype.fillHistory = co(function* fillHistory(tx) {
|
|||||||
|
|
||||||
prev = yield this.getTX(prevout.hash);
|
prev = yield this.getTX(prevout.hash);
|
||||||
|
|
||||||
if (!prev)
|
if (!prev || prevout.index >= prev.outputs.length) {
|
||||||
continue;
|
input.coin = null;
|
||||||
|
found = false;
|
||||||
if (prevout.index >= prev.outputs.length)
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
input.coin = Coin.fromTX(prev, prevout.index);
|
input.coin = Coin.fromTX(prev, prevout.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -290,7 +290,7 @@ Mempool.prototype._reset = function reset() {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Mempool.prototype.limitMempoolSize = function limitMempoolSize(entryHash) {
|
Mempool.prototype.limitSize = function limitSize(entryHash) {
|
||||||
var trimmed = false;
|
var trimmed = false;
|
||||||
var i, hashes, hash, end, entry;
|
var i, hashes, hash, end, entry;
|
||||||
|
|
||||||
@ -717,7 +717,7 @@ Mempool.prototype._addTX = co(function* _addTX(tx) {
|
|||||||
yield this.addEntry(entry);
|
yield this.addEntry(entry);
|
||||||
|
|
||||||
// Trim size if we're too big.
|
// Trim size if we're too big.
|
||||||
if (this.limitMempoolSize(hash)) {
|
if (this.limitSize(hash)) {
|
||||||
throw new VerifyError(tx,
|
throw new VerifyError(tx,
|
||||||
'insufficientfee',
|
'insufficientfee',
|
||||||
'mempool full',
|
'mempool full',
|
||||||
@ -911,9 +911,9 @@ Mempool.prototype.verify = co(function* verify(entry) {
|
|||||||
// Script verification.
|
// Script verification.
|
||||||
try {
|
try {
|
||||||
yield this.verifyInputs(tx, flags1);
|
yield this.verifyInputs(tx, flags1);
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
if (tx.hasWitness())
|
if (tx.hasWitness())
|
||||||
throw error;
|
throw err;
|
||||||
|
|
||||||
// Try without segwit and cleanstack.
|
// Try without segwit and cleanstack.
|
||||||
result = yield this.verifyResult(tx, flags2);
|
result = yield this.verifyResult(tx, flags2);
|
||||||
@ -921,7 +921,7 @@ Mempool.prototype.verify = co(function* verify(entry) {
|
|||||||
// If it failed, the first verification
|
// If it failed, the first verification
|
||||||
// was the only result we needed.
|
// was the only result we needed.
|
||||||
if (!result)
|
if (!result)
|
||||||
throw error;
|
throw err;
|
||||||
|
|
||||||
// If it succeeded, segwit may be causing the
|
// If it succeeded, segwit may be causing the
|
||||||
// failure. Try with segwit but without cleanstack.
|
// failure. Try with segwit but without cleanstack.
|
||||||
@ -929,11 +929,11 @@ Mempool.prototype.verify = co(function* verify(entry) {
|
|||||||
|
|
||||||
// Cleanstack was causing the failure.
|
// Cleanstack was causing the failure.
|
||||||
if (result)
|
if (result)
|
||||||
throw error;
|
throw err;
|
||||||
|
|
||||||
// Do not insert into reject cache.
|
// Do not insert into reject cache.
|
||||||
error.malleated = true;
|
err.malleated = true;
|
||||||
throw error;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paranoid checks.
|
// Paranoid checks.
|
||||||
@ -1313,89 +1313,6 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, missing) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Spend coins for transaction.
|
|
||||||
* @param {TX} tx
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Mempool.prototype.injectCoins = function injectCoins(tx, view) {
|
|
||||||
var missing = [];
|
|
||||||
var i, input, prevout, coin;
|
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
|
||||||
input = tx.inputs[i];
|
|
||||||
prevout = input.prevout;
|
|
||||||
coin = view.getCoin(prevout.hash, prevout.index);
|
|
||||||
|
|
||||||
if (!coin) {
|
|
||||||
missing.push(prevout.hash);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.coin = coin;
|
|
||||||
}
|
|
||||||
|
|
||||||
return missing;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store an orphaned transaction.
|
|
||||||
* @param {TX} tx
|
|
||||||
*/
|
|
||||||
|
|
||||||
Mempool.prototype.maybeStoreOrphan = function maybeStoreOrphan(tx) {
|
|
||||||
var missing = {};
|
|
||||||
var i, hash, input, prev;
|
|
||||||
|
|
||||||
if (tx.getWeight() > constants.tx.MAX_WEIGHT) {
|
|
||||||
this.logger.debug('Ignoring large orphan: %s', tx.txid());
|
|
||||||
if (!tx.hasWitness())
|
|
||||||
this.rejects.add(tx.hash());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
|
||||||
input = tx.inputs[i];
|
|
||||||
|
|
||||||
if (input.coin)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (this.hasReject(input.prevout.hash)) {
|
|
||||||
this.logger.debug('Not storing orphan %s (rejected parents).', tx.txid());
|
|
||||||
this.rejects.add(tx.hash());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
missing[input.prevout.hash] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = tx.hash('hex');
|
|
||||||
missing = Object.keys(missing);
|
|
||||||
|
|
||||||
assert(missing.length > 0);
|
|
||||||
|
|
||||||
for (i = 0; i < missing.length; i++) {
|
|
||||||
prev = missing[i];
|
|
||||||
|
|
||||||
if (!this.waiting[prev])
|
|
||||||
this.waiting[prev] = [];
|
|
||||||
|
|
||||||
this.waiting[prev].push(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.orphans[hash] = new Orphan(tx, missing.length);
|
|
||||||
this.totalOrphans++;
|
|
||||||
|
|
||||||
this.logger.debug('Added orphan %s to mempool.', tx.txid());
|
|
||||||
|
|
||||||
this.emit('add orphan', tx);
|
|
||||||
|
|
||||||
this.limitOrphans();
|
|
||||||
|
|
||||||
return missing;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Potentially resolve any transactions
|
* Potentially resolve any transactions
|
||||||
* that redeem the passed-in transaction.
|
* that redeem the passed-in transaction.
|
||||||
@ -1521,165 +1438,6 @@ Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill a transaction with all available transaction outputs
|
|
||||||
* in the mempool. This differs from {@link Mempool#fillCoins}
|
|
||||||
* in that it will fill with all historical coins and not
|
|
||||||
* just unspent coins.
|
|
||||||
* @param {TX} tx
|
|
||||||
*/
|
|
||||||
|
|
||||||
Mempool.prototype.fillHistory = function fillHistory(tx) {
|
|
||||||
var found = true;
|
|
||||||
var i, input, prevout, prev;
|
|
||||||
|
|
||||||
if (tx.isCoinbase())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
|
||||||
input = tx.inputs[i];
|
|
||||||
prevout = input.prevout;
|
|
||||||
prev = this.getTX(prevout.hash);
|
|
||||||
|
|
||||||
if (!prev) {
|
|
||||||
input.coin = null;
|
|
||||||
found = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevout.index >= prev.outputs.length) {
|
|
||||||
input.coin = null;
|
|
||||||
found = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.coin = Coin.fromTX(prev, prevout.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill a transaction with all available (unspent) coins
|
|
||||||
* in the mempool.
|
|
||||||
* @param {TX} tx
|
|
||||||
*/
|
|
||||||
|
|
||||||
Mempool.prototype.fillCoins = function fillCoins(tx) {
|
|
||||||
var found = true;
|
|
||||||
var i, input, prevout, coin;
|
|
||||||
|
|
||||||
if (tx.isCoinbase())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
|
||||||
input = tx.inputs[i];
|
|
||||||
prevout = input.prevout;
|
|
||||||
coin = this.getCoin(prevout.hash, prevout.index);
|
|
||||||
|
|
||||||
if (!coin) {
|
|
||||||
input.coin = null;
|
|
||||||
found = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.coin = coin;
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill transaction with all unspent _and spent_
|
|
||||||
* coins. Similar to {@link Mempool#fillHistory}
|
|
||||||
* except that it will also fill with coins
|
|
||||||
* from the blockchain as well.
|
|
||||||
* @param {TX} tx
|
|
||||||
* @returns {Promise} - Returns {@link TX}.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Mempool.prototype.fillAllHistory = function fillAllHistory(tx) {
|
|
||||||
this.fillHistory(tx);
|
|
||||||
|
|
||||||
if (tx.hasCoins())
|
|
||||||
return Promise.resolve(tx);
|
|
||||||
|
|
||||||
return this.chain.db.fillHistory(tx);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill transaction with all unspent
|
|
||||||
* coins. Similar to {@link Mempool#fillCoins}
|
|
||||||
* except that it will also fill with coins
|
|
||||||
* from the blockchain as well.
|
|
||||||
* @param {TX} tx
|
|
||||||
* @returns {Promise} - Returns {@link TX}.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Mempool.prototype.fillAllCoins = co(function* fillAllCoins(tx) {
|
|
||||||
var found = true;
|
|
||||||
var i, input, hash, index, coin;
|
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
|
||||||
input = tx.inputs[i];
|
|
||||||
hash = input.prevout.hash;
|
|
||||||
index = input.prevout.index;
|
|
||||||
|
|
||||||
coin = this.getCoin(hash, index);
|
|
||||||
|
|
||||||
if (coin) {
|
|
||||||
input.coin = coin;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isSpent(hash, index)) {
|
|
||||||
input.coin = null;
|
|
||||||
found = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
coin = yield this.chain.db.getCoin(hash, index);
|
|
||||||
|
|
||||||
if (!coin) {
|
|
||||||
input.coin = null;
|
|
||||||
found = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.coin = coin;
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get coin viewpoint.
|
|
||||||
* @param {TX} tx
|
|
||||||
* @returns {Promise} - Returns {@link CoinView}.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Mempool.prototype.getCoinViewSlow = co(function* getCoinViewSlow(tx) {
|
|
||||||
var view = yield this.chain.db.getCoinView(tx);
|
|
||||||
var entries = view.toArray();
|
|
||||||
var i, coins, entry;
|
|
||||||
|
|
||||||
for (i = 0; i < entries.length; i++) {
|
|
||||||
coins = entries[i];
|
|
||||||
|
|
||||||
if (!coins.isEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
entry = this.getEntry(coins.hash);
|
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
view.addTX(entry.tx, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return view;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get coin viewpoint.
|
* Get coin viewpoint.
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
@ -1715,6 +1473,32 @@ Mempool.prototype.getCoinView = co(function* getCoinView(tx) {
|
|||||||
return view;
|
return view;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spend coins for transaction.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mempool.prototype.injectCoins = function injectCoins(tx, view) {
|
||||||
|
var missing = [];
|
||||||
|
var i, input, prevout, coin;
|
||||||
|
|
||||||
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
|
input = tx.inputs[i];
|
||||||
|
prevout = input.prevout;
|
||||||
|
coin = view.getCoin(prevout.hash, prevout.index);
|
||||||
|
|
||||||
|
if (!coin) {
|
||||||
|
missing.push(prevout.hash);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.coin = coin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return missing;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a snapshot of all transaction hashes in the mempool. Used
|
* Get a snapshot of all transaction hashes in the mempool. Used
|
||||||
* for generating INV packets in response to MEMPOOL packets.
|
* for generating INV packets in response to MEMPOOL packets.
|
||||||
@ -1960,6 +1744,106 @@ Mempool.prototype.getSize = function getSize() {
|
|||||||
return this.size;
|
return this.size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill transaction with all unspent _and spent_
|
||||||
|
* coins. Similar to {@link Mempool#fillHistory}
|
||||||
|
* except that it will also fill with coins
|
||||||
|
* from the blockchain as well.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @returns {Promise} - Returns {@link TX}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mempool.prototype.fillHistory = co(function* fillHistory(tx) {
|
||||||
|
var found = true;
|
||||||
|
var i, input, prevout, prev;
|
||||||
|
|
||||||
|
if (tx.isCoinbase())
|
||||||
|
return found;
|
||||||
|
|
||||||
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
|
input = tx.inputs[i];
|
||||||
|
prevout = input.prevout;
|
||||||
|
|
||||||
|
if (input.coin)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
prev = this.getTX(prevout.hash);
|
||||||
|
|
||||||
|
if (prev) {
|
||||||
|
if (prevout.index >= prev.outputs.length) {
|
||||||
|
input.coin = null;
|
||||||
|
found = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
input.coin = Coin.fromTX(prev, prevout.index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = yield this.chain.db.getTX(prevout.hash);
|
||||||
|
|
||||||
|
if (!prev || prevout.index >= prev.outputs.length) {
|
||||||
|
input.coin = null;
|
||||||
|
found = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.coin = Coin.fromTX(prev, prevout.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill transaction with all unspent
|
||||||
|
* coins. Similar to {@link Mempool#fillCoins}
|
||||||
|
* except that it will also fill with coins
|
||||||
|
* from the blockchain as well.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @returns {Promise} - Returns {@link TX}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mempool.prototype.fillCoins = co(function* fillCoins(tx) {
|
||||||
|
var found = true;
|
||||||
|
var i, input, hash, index, coin;
|
||||||
|
|
||||||
|
if (tx.isCoinbase())
|
||||||
|
return found;
|
||||||
|
|
||||||
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
|
input = tx.inputs[i];
|
||||||
|
hash = input.prevout.hash;
|
||||||
|
index = input.prevout.index;
|
||||||
|
|
||||||
|
if (input.coin)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
coin = this.getCoin(hash, index);
|
||||||
|
|
||||||
|
if (coin) {
|
||||||
|
input.coin = coin;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isSpent(hash, index)) {
|
||||||
|
input.coin = null;
|
||||||
|
found = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
coin = yield this.chain.db.getCoin(hash, index);
|
||||||
|
|
||||||
|
if (!coin) {
|
||||||
|
input.coin = null;
|
||||||
|
found = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.coin = coin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Address Index
|
* Address Index
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -422,8 +422,10 @@ FullNode.prototype.getCoinsByAddress = co(function* getCoinsByAddress(addresses)
|
|||||||
coin = blockCoins[i];
|
coin = blockCoins[i];
|
||||||
spent = this.mempool.isSpent(coin.hash, coin.index);
|
spent = this.mempool.isSpent(coin.hash, coin.index);
|
||||||
|
|
||||||
if (!spent)
|
if (spent)
|
||||||
coins.push(coin);
|
continue;
|
||||||
|
|
||||||
|
coins.push(coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
return coins;
|
return coins;
|
||||||
@ -492,7 +494,7 @@ FullNode.prototype.isSpent = function isSpent(hash, index) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
FullNode.prototype.fillCoins = function fillCoins(tx) {
|
FullNode.prototype.fillCoins = function fillCoins(tx) {
|
||||||
return this.mempool.fillAllCoins(tx);
|
return this.mempool.fillCoins(tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -503,7 +505,7 @@ FullNode.prototype.fillCoins = function fillCoins(tx) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
FullNode.prototype.fillHistory = function fillHistory(tx) {
|
FullNode.prototype.fillHistory = function fillHistory(tx) {
|
||||||
return this.mempool.fillAllHistory(tx);
|
return this.mempool.fillHistory(tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user