All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bitcoinj.coinjoin.CoinJoinClientQueueManager Maven / Gradle / Ivy

There is a newer version: 21.1.2
Show newest version
/*
 * Copyright (c) 2022 Dash Core Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.bitcoinj.coinjoin;

import org.bitcoinj.core.Context;
import org.bitcoinj.core.MasternodeSync;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Utils;
import org.bitcoinj.evolution.Masternode;
import org.bitcoinj.evolution.SimplifiedMasternodeList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.function.Predicate;

import static org.bitcoinj.coinjoin.CoinJoinConstants.COINJOIN_EXTRA;

public class CoinJoinClientQueueManager extends CoinJoinBaseManager {
    private final Context context;
    private final Logger log = LoggerFactory.getLogger(CoinJoinClientManager.class);
    private final HashMap spammingMasternodes = new HashMap();

    public CoinJoinClientQueueManager(Context context) {
        super();
        this.context = context;
    }

    public void processDSQueue(Peer from, CoinJoinQueue dsq, boolean enable_bip61) {
        boolean isLocked = queueLock.tryLock();
        if (isLocked) {
            try {
                // process every dsq only once
                for (CoinJoinQueue q : coinJoinQueue) {
                    if (q == dsq) {
                        return;
                    }
                    if (q.isReady() == dsq.isReady() && q.getProTxHash().equals(dsq.getProTxHash())) {
                        // no way the same mn can send another dsq with the same readiness this soon
                        if (!spammingMasternodes.containsKey(dsq.getProTxHash())) {
                            spammingMasternodes.put(dsq.getProTxHash(), Utils.currentTimeMillis());
                            log.info(COINJOIN_EXTRA, "coinjoin: DSQUEUE: Peer {} is sending WAY too many dsq messages for a masternode {}", from.getAddress().getAddr(), dsq.getProTxHash());
                        }
                        return;
                    }
                }
            } // cs_vecqueue
            finally {
                queueLock.unlock();
            }


            log.info(COINJOIN_EXTRA, "coinjoin: DSQUEUE -- {} new", dsq);

            if (dsq.isTimeOutOfBounds())
                return;

            SimplifiedMasternodeList mnList = context.masternodeListManager.getListAtChainTip();
            Masternode dmn = mnList.getMN(dsq.getProTxHash());
            if (dmn == null)
                return;

            if (!dsq.checkSignature(dmn.getPubKeyOperator())) {
                // add 10 points to ban score
                return;
            }

            // if the queue is ready, submit if we can
            if (dsq.isReady() && isTrySubmitDenominate(dmn)) {
                log.info("coinjoin: DSQUEUE: {} is ready on masternode {}", dsq, dmn.getService());
            } else {
                long nLastDsq = context.masternodeMetaDataManager.getMetaInfo(dmn.getProTxHash()).getLastDsq();
                long nDsqThreshold = context.masternodeMetaDataManager.getDsqThreshold(dmn.getProTxHash(), mnList.getValidMNsCount());
                log.info(COINJOIN_EXTRA, "coinjoin: DSQUEUE -- lastDsq: {}  dsqThreshold: {}  dsqCount: {}",
                        nLastDsq, nDsqThreshold, context.masternodeMetaDataManager.getDsqCount());
                // don't allow a few nodes to dominate the queuing process
                if (nLastDsq != 0 && nDsqThreshold > context.masternodeMetaDataManager.getDsqCount()) {
                    if (!spammingMasternodes.containsKey(dsq.getProTxHash())) {
                        spammingMasternodes.put(dsq.getProTxHash(), Utils.currentTimeMillis());
                        log.info(COINJOIN_EXTRA, "coinjoin: DSQUEUE: Masternode {} is sending too many dsq messages", dmn.getProTxHash());
                    }
                    return;
                }

                context.masternodeMetaDataManager.allowMixing(dmn.getProTxHash());

                log.info("coinjoin: DSQUEUE: new {} from masternode {}", dsq, dmn.getService().getAddr());

                context.coinJoinManager.coinJoinClientManagers.values().stream().anyMatch(new Predicate() {
                    @Override
                    public boolean test(CoinJoinClientManager coinJoinClientManager) {
                        return coinJoinClientManager.markAlreadyJoinedQueueAsTried(dsq);
                    }
                });

                if (queueLock.tryLock()) {
                    try {
                        coinJoinQueue.add(dsq);
                    } finally {
                        queueLock.unlock();
                    }
                }
            }
        } // not locked
    }

    private boolean isTrySubmitDenominate(Masternode dmn) {
        return context.coinJoinManager.coinJoinClientManagers.values().stream().anyMatch(new Predicate() {
            @Override
            public boolean test(CoinJoinClientManager coinJoinClientManager) {
                return coinJoinClientManager.trySubmitDenominate(dmn.getService());
            }
        });
    }

    public void doMaintenance() {

        if (!CoinJoinClientOptions.isEnabled())
            return;
        if (context.masternodeSync == null)
            return;

        if (context.masternodeSync.hasSyncFlag(MasternodeSync.SYNC_FLAGS.SYNC_GOVERNANCE) &&
                !context.masternodeSync.isBlockchainSynced())
            return;

        checkQueue();

        spammingMasternodes.entrySet().removeIf(entry -> entry.getValue() + CoinJoinConstants.COINJOIN_QUEUE_TIMEOUT * 1000 < Utils.currentTimeMillis());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy