co.rsk.mine.MinerUtils.java Source code

Java tutorial

Introduction

Here is the source code for co.rsk.mine.MinerUtils.java

Source

/*
 * This file is part of RskJ
 * Copyright (C) 2017 RSK Labs Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package co.rsk.mine;

import co.rsk.bitcoinj.core.BtcTransaction;
import co.rsk.bitcoinj.core.NetworkParameters;
import co.rsk.config.RskMiningConstants;
import co.rsk.core.Coin;
import co.rsk.core.RskAddress;
import co.rsk.core.bc.PendingState;
import co.rsk.crypto.Keccak256;
import co.rsk.remasc.RemascTransaction;
import org.ethereum.config.BlockchainNetConfig;
import org.ethereum.core.TransactionPool;
import org.ethereum.core.Repository;
import org.ethereum.core.Transaction;
import org.ethereum.rpc.TypeConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.bouncycastle.util.Arrays;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.*;
import java.util.function.Function;

public class MinerUtils {

    private static final Logger logger = LoggerFactory.getLogger("minerserver");

    public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransaction(
            co.rsk.bitcoinj.core.NetworkParameters params, MinerWork work) {
        return getBitcoinMergedMiningCoinbaseTransaction(params,
                TypeConverter.stringHexToByteArray(work.getBlockHashForMergedMining()));
    }

    public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransaction(
            co.rsk.bitcoinj.core.NetworkParameters params, byte[] blockHashForMergedMining) {
        co.rsk.bitcoinj.core.BtcTransaction coinbaseTransaction = new co.rsk.bitcoinj.core.BtcTransaction(params);
        //Add a random number of random bytes before the RSK tag
        SecureRandom random = new SecureRandom();
        byte[] prefix = new byte[random.nextInt(1000)];
        random.nextBytes(prefix);
        byte[] bytes = Arrays.concatenate(prefix, RskMiningConstants.RSK_TAG, blockHashForMergedMining);
        // Add the Tag to the scriptSig of first input
        co.rsk.bitcoinj.core.TransactionInput ti = new co.rsk.bitcoinj.core.TransactionInput(params,
                coinbaseTransaction, bytes);
        coinbaseTransaction.addInput(ti);
        ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
        co.rsk.bitcoinj.core.BtcECKey key = new co.rsk.bitcoinj.core.BtcECKey();
        try {
            co.rsk.bitcoinj.script.Script.writeBytes(scriptPubKeyBytes, key.getPubKey());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        scriptPubKeyBytes.write(co.rsk.bitcoinj.script.ScriptOpCodes.OP_CHECKSIG);
        coinbaseTransaction.addOutput(new co.rsk.bitcoinj.core.TransactionOutput(params, coinbaseTransaction,
                co.rsk.bitcoinj.core.Coin.valueOf(50, 0), scriptPubKeyBytes.toByteArray()));
        return coinbaseTransaction;
    }

    public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransactionWithTwoTags(
            co.rsk.bitcoinj.core.NetworkParameters params, MinerWork work, MinerWork work2) {
        return getBitcoinMergedMiningCoinbaseTransactionWithTwoTags(params,
                TypeConverter.stringHexToByteArray(work.getBlockHashForMergedMining()),
                TypeConverter.stringHexToByteArray(work2.getBlockHashForMergedMining()));
    }

    public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransactionWithTwoTags(
            co.rsk.bitcoinj.core.NetworkParameters params, byte[] blockHashForMergedMining1,
            byte[] blockHashForMergedMining2) {
        co.rsk.bitcoinj.core.BtcTransaction coinbaseTransaction = new co.rsk.bitcoinj.core.BtcTransaction(params);
        //Add a random number of random bytes before the RSK tag
        SecureRandom random = new SecureRandom();
        byte[] prefix = new byte[random.nextInt(1000)];
        random.nextBytes(prefix);

        byte[] bytes0 = Arrays.concatenate(RskMiningConstants.RSK_TAG, blockHashForMergedMining1);
        // addsecond tag
        byte[] bytes1 = Arrays.concatenate(bytes0, RskMiningConstants.RSK_TAG, blockHashForMergedMining2);

        co.rsk.bitcoinj.core.TransactionInput ti = new co.rsk.bitcoinj.core.TransactionInput(params,
                coinbaseTransaction, prefix);
        coinbaseTransaction.addInput(ti);
        ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
        co.rsk.bitcoinj.core.BtcECKey key = new co.rsk.bitcoinj.core.BtcECKey();
        try {
            co.rsk.bitcoinj.script.Script.writeBytes(scriptPubKeyBytes, key.getPubKey());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        scriptPubKeyBytes.write(co.rsk.bitcoinj.script.ScriptOpCodes.OP_CHECKSIG);
        coinbaseTransaction.addOutput(new co.rsk.bitcoinj.core.TransactionOutput(params, coinbaseTransaction,
                co.rsk.bitcoinj.core.Coin.valueOf(50, 0), scriptPubKeyBytes.toByteArray()));
        // add opreturn output with two tags
        ByteArrayOutputStream output2Bytes = new ByteArrayOutputStream();
        output2Bytes.write(co.rsk.bitcoinj.script.ScriptOpCodes.OP_RETURN);

        try {
            co.rsk.bitcoinj.script.Script.writeBytes(output2Bytes, bytes1);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        coinbaseTransaction.addOutput(new co.rsk.bitcoinj.core.TransactionOutput(params, coinbaseTransaction,
                co.rsk.bitcoinj.core.Coin.valueOf(1), output2Bytes.toByteArray()));

        return coinbaseTransaction;
    }

    public static co.rsk.bitcoinj.core.BtcBlock getBitcoinMergedMiningBlock(
            co.rsk.bitcoinj.core.NetworkParameters params, BtcTransaction transaction) {
        return getBitcoinMergedMiningBlock(params, Collections.singletonList(transaction));
    }

    public static co.rsk.bitcoinj.core.BtcBlock getBitcoinMergedMiningBlock(
            co.rsk.bitcoinj.core.NetworkParameters params, List<BtcTransaction> transactions) {
        co.rsk.bitcoinj.core.Sha256Hash prevBlockHash = co.rsk.bitcoinj.core.Sha256Hash.ZERO_HASH;
        long time = System.currentTimeMillis() / 1000L;
        long difficultyTarget = co.rsk.bitcoinj.core.Utils.encodeCompactBits(params.getMaxTarget());
        return new co.rsk.bitcoinj.core.BtcBlock(params,
                params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.CURRENT), prevBlockHash, null, time,
                difficultyTarget, 0, transactions);
    }

    /**
     * Takes in a proofBuilderFunction (e.g. buildFromTxHashes)
     * and executes it on the builder corresponding to this block number.
     */
    public static byte[] buildMerkleProof(BlockchainNetConfig blockchainConfig,
            Function<MerkleProofBuilder, byte[]> proofBuilderFunction, long blockNumber) {
        if (blockchainConfig.getConfigForBlock(blockNumber).isRskip92()) {
            return proofBuilderFunction.apply(new Rskip92MerkleProofBuilder());
        } else {
            return proofBuilderFunction.apply(new GenesisMerkleProofBuilder());
        }
    }

    public List<org.ethereum.core.Transaction> getAllTransactions(TransactionPool transactionPool) {

        List<Transaction> txs = transactionPool.getPendingTransactions();

        return PendingState.sortByPriceTakingIntoAccountSenderAndNonce(txs);
    }

    public List<org.ethereum.core.Transaction> filterTransactions(List<Transaction> txsToRemove,
            List<Transaction> txs, Map<RskAddress, BigInteger> accountNonces, Repository originalRepo,
            Coin minGasPrice) {
        List<org.ethereum.core.Transaction> txsResult = new ArrayList<>();
        for (org.ethereum.core.Transaction tx : txs) {
            try {
                Keccak256 hash = tx.getHash();
                Coin txValue = tx.getValue();
                BigInteger txNonce = new BigInteger(1, tx.getNonce());
                RskAddress txSender = tx.getSender();
                logger.debug("Examining tx={} sender: {} value: {} nonce: {}", hash, txSender, txValue, txNonce);

                BigInteger expectedNonce;

                if (accountNonces.containsKey(txSender)) {
                    expectedNonce = accountNonces.get(txSender).add(BigInteger.ONE);
                } else {
                    expectedNonce = originalRepo.getNonce(txSender);
                }

                if (!(tx instanceof RemascTransaction) && tx.getGasPrice().compareTo(minGasPrice) < 0) {
                    logger.warn("Rejected tx={} because of low gas account {}, removing tx from pending state.",
                            hash, txSender);

                    txsToRemove.add(tx);
                    continue;
                }

                if (!expectedNonce.equals(txNonce)) {
                    logger.warn("Invalid nonce, expected {}, found {}, tx={}", expectedNonce, txNonce, hash);
                    continue;
                }

                accountNonces.put(txSender, txNonce);

                logger.debug("Accepted tx={} sender: {} value: {} nonce: {}", hash, txSender, txValue, txNonce);
            } catch (Exception e) {
                // Txs that can't be selected by any reason should be removed from pending state
                logger.warn(String.format("Error when processing tx=%s", tx.getHash()), e);
                if (txsToRemove != null) {
                    txsToRemove.add(tx);
                } else {
                    logger.error("Can't remove invalid txs from pending state.");
                }
                continue;
            }

            txsResult.add(tx);
        }

        logger.debug("Ending getTransactions {}", txsResult.size());

        return txsResult;
    }
}