co.rsk.blockchain.utils.BlockGenerator.java Source code

Java tutorial

Introduction

Here is the source code for co.rsk.blockchain.utils.BlockGenerator.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.blockchain.utils;

import co.rsk.config.RskSystemProperties;
import co.rsk.core.bc.BlockChainImpl;
import co.rsk.peg.PegTestUtils;
import co.rsk.peg.simples.SimpleRskTransaction;
import co.rsk.mine.MinimumGasPriceCalculator;
import co.rsk.peg.simples.SimpleBlock;

import co.rsk.trie.TrieImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.ethereum.core.*;
import org.ethereum.core.genesis.InitialAddressState;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.ByteArrayWrapper;
import co.rsk.trie.Trie;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.spongycastle.pqc.math.linearalgebra.ByteUtils;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;

import java.util.List;
import java.util.Map;

import static org.ethereum.core.Genesis.getZeroHash;
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
import static org.ethereum.util.ByteUtil.wrap;

/**
 * Created by ajlopez on 5/10/2016.
 */
public class BlockGenerator {
    private static final byte[] EMPTY_LIST_HASH = HashUtil.sha3(RLP.encodeList());

    private static byte[] EMPTY_BYTE_ARRAY = new byte[0];
    private static int count = 0;

    // from bcValidBlockTest.json
    private static String genesisRLP = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0";

    private static String[] blockRlps = { genesisRLP,
            "f902b4f902afa0abff92b32e43e9f34eda3fa7fe5359cb06871b172226a829daa9af22d1fac2cea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949efa02278cc63dc612c174976f11037d382f8b67a0c5d6ad68162cb8f04ef7afc8bf74558a19bceaf334b0497bd8d3c86c24de9f9da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0a8f57ab26ee2c88d15f6bd20f052dbc76a1f4a0b55d214a88f4b8201b8840736b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018407fe000080845730e70e808080b85004000000f08613eff431f121c059541e38594a190d6b0e46d2cb2bb52dc6b692020000003b8cacca5ed46c229a777c7c55ddfefed2b78ccb4ce6df07ecb1e36bde29f4741ee73057ffff7f20005a40aca701000000013b8cacca5ed46c229a777c7c55ddfefed2b78ccb4ce6df07ecb1e36bde29f4740101b86100000000000000801f506f4152ccdb46a5e162488b24647e750c88073c56530d2154475fdc8c4cb4ac00000000000000002b6a524f4f5453544f434b3a1ff8eae11ecb36803b0fd23bd31490bd1054e078852a0f975428871c42bef40900000000800ac0c0",
            "f902b4f902afa0c15c503127c6c70f53666806a336ab7600c5d7aa86bf2bd9149acb48b3353ffba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949efa02278cc63dc612c174976f11037d382f8b67a0feac8b5b3fdaad27bc3a289646b6c44464c4b89ce30d1649cfe39e2b26323526a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0e2bdde9667f20119e55bc4d60dd80c14fca25caf985b8523e20c649b1113b06eb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000028407fc008080845730e726808080b85004000000f08613eff431f121c059541e38594a190d6b0e46d2cb2bb52dc6b69202000000899e557890240f889575a7dbb3249e6a9cb3df8933ffd29fd88b0a559229b11f28e73057ffff7f20804a9c66a70100000001899e557890240f889575a7dbb3249e6a9cb3df8933ffd29fd88b0a559229b11f0101b861000000000000008034d70d272689bd240d2c25c5d15b5bb7242c6979011ec2167faad2b90c079c2fac00000000000000002b6a524f4f5453544f434b3acb21587449c2d39b94bf06c2b81ea74d83ad1b4de7d425cfce01253796b2d85a00000000800ac0c0",
            "f9031ff902b3a0c5ff6a7292616ea38273cbde89520a24b9e117cab75d0439313fbe539b351ebca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949efa02278cc63dc612c174976f11037d382f8b67a0b36f7def24114af7bc148c1b911e5a75fc7e7a8b6f05f9dd8aebe5204aafb41da00254dfb821f03ebf1660588345960c9528e214998f6ce5c996136410554d4a5fa0f0c28d912ccc2e025463f937def58f171f89f274367cdb6b13aaf604b202da0db901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020040038407fa017f825208845730e72f808080b85004000000f08613eff431f121c059541e38594a190d6b0e46d2cb2bb52dc6b69202000000148469a712391f2d0fd4394b39a2f8fa878f37b63268a26f6b1403a9c45a2cfc30e73057ffff7f20800911a8a70100000001148469a712391f2d0fd4394b39a2f8fa878f37b63268a26f6b1403a9c45a2cfc0101b861000000000000008031377ae680b963a4968b540f3a6aa00651fa1ac60c07d39f5c3ef715759096c2ac00000000000000002b6a524f4f5453544f434b3ac25da85d2292da795489ab2eb6b534eb3def0cd6e29f1ddbe09f26d74dca325a000000008252080af866f864800182520894e42d40b27a5f18685520f0f22aea086181ed61508502540be400801ca05aaaf420781ee3ca97df2543809edb52e41c95d2cfbda16d8fc6772abeae221ca032e8c5f3ed3de052c55b57f5ea3d7c882dabb8c4f758f990579236af7c2fe4e6c0",
            "f902b4f902afa02224b34b6bd4a0b4a4dc2b9aa67d86dd401a4c180d519022f85e500ff82f7637a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347949efa02278cc63dc612c174976f11037d382f8b67a0bbef991ea1691a0ee9eeebeda57dde7fe3f80f589f7abd47bf16d8cd04b964c3a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0249b2f538c284002a2068409d2cbdcba3a74072cea3dcaf1bcef415e86acbaf6b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020080048407f802fe80845730e732808080b85004000000f08613eff431f121c059541e38594a190d6b0e46d2cb2bb52dc6b69202000000e8d9789a4509cb1f3d775d267b7f2fa7b5291f56717dc962dc319e9d706c84ee33e73057ffff7f2080012abca70100000001e8d9789a4509cb1f3d775d267b7f2fa7b5291f56717dc962dc319e9d706c84ee0101b861000000000000008010a3283751f55cfc66e4f5e3691330ef9fe39b86852b98e0fef969ab2bfb16cbac00000000000000002b6a524f4f5453544f434b3a772c6a4f889a9cf4bc7ec380c9014dbf0f8cb529155282a4ea78ccb18f18fd7600000000800ac0c0" };

    public static Genesis getGenesisBlock() {
        return new Genesis(Hex.decode(genesisRLP));
    }

    static final boolean isRsk = true;

    public static Block getNewGenesisBlock(long initialGasLimit, Map<byte[], BigInteger> preMineMap) {
        return getNewGenesisBlock(initialGasLimit, preMineMap, (byte) 0);
    }

    public static Block getNewGenesisBlock(long initialGasLimit, Map<byte[], BigInteger> preMineMap,
            byte difficultyByte) {

        byte[] nonce = new byte[] { 0 };
        byte[] difficulty = new byte[] { difficultyByte };
        byte[] mixHash = new byte[] { 0 };

        /* Unimportant address. Because there is no subsidy
        ECKey ecKey;
        byte[] address;
        SecureRandom rand =new InsecureRandom(0);
        ecKey = new ECKey(rand);
        address = ecKey.getAddress();
        */
        byte[] coinbase = Hex.decode("e94aef644e428941ee0a3741f28d80255fddba7f");

        long timestamp = 0; // predictable timeStamp

        byte[] parentHash = EMPTY_BYTE_ARRAY;
        byte[] extraData = EMPTY_BYTE_ARRAY;

        long gasLimit = initialGasLimit;

        byte[] bitcoinMergedMiningHeader = null;
        byte[] bitcoinMergedMiningMerkleProof = null;
        byte[] bitcoinMergedMiningCoinbaseTransaction = null;

        Genesis genesis = new Genesis(parentHash, EMPTY_LIST_HASH, coinbase, getZeroHash(), difficulty, 0, gasLimit,
                0, timestamp, extraData, mixHash, nonce, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof,
                bitcoinMergedMiningCoinbaseTransaction, BigInteger.valueOf(100L).toByteArray());
        if (preMineMap != null) {
            Map<ByteArrayWrapper, InitialAddressState> preMineMap2 = generatePreMine(preMineMap);
            genesis.setPremine(preMineMap2);

            byte[] rootHash = generateRootHash(preMineMap2);
            genesis.setStateRoot(rootHash);

        }
        return genesis;
    }

    private static byte[] generateRootHash(Map<ByteArrayWrapper, InitialAddressState> premine) {
        Trie state = new TrieImpl(null, true);

        for (ByteArrayWrapper key : premine.keySet())
            state = state.put(key.getData(), premine.get(key).getAccountState().getEncoded());

        return state.getHash();
    }

    private static Map<ByteArrayWrapper, InitialAddressState> generatePreMine(Map<byte[], BigInteger> alloc) {
        Map<ByteArrayWrapper, InitialAddressState> premine = new HashMap<>();
        for (byte[] key : alloc.keySet()) {
            AccountState acctState = new AccountState(BigInteger.valueOf(0), alloc.get(key));
            premine.put(wrap(key), new InitialAddressState(acctState, null));
        }

        return premine;
    }

    public static Block getBlock(int number) {
        return new Block(Hex.decode(blockRlps[number]));
    }

    public static Block createChildBlock(Block parent) {
        return createChildBlock(parent, 0);
    }

    public static Block createChildBlock(Block parent, List<Transaction> txs, byte[] stateRoot) {
        return createChildBlock(parent, txs, stateRoot, parent.getCoinbase());
    }

    public static Block createChildBlock(Block parent, List<Transaction> txs, byte[] stateRoot, byte[] coinbase) {
        Bloom logBloom = new Bloom();

        if (txs == null)
            txs = new ArrayList<>();

        return new Block(parent.getHash(), // parent hash
                EMPTY_LIST_HASH, // uncle hash
                coinbase, // coinbase
                logBloom.getData(), // logs bloom
                parent.getDifficulty(), // difficulty
                parent.getNumber() + 1, parent.getGasLimit(), parent.getGasUsed(), parent.getTimestamp() + ++count,
                EMPTY_BYTE_ARRAY, // extraData
                EMPTY_BYTE_ARRAY, // mixHash
                BigInteger.ZERO.toByteArray(), // provisory nonce
                EMPTY_TRIE_HASH, // receipts root
                BlockChainImpl.calcTxTrie(txs), // transaction root
                stateRoot, //EMPTY_TRIE_HASH,   // state root
                txs, // transaction list
                null, // uncle list
                null);
    }

    public static Block createChildBlock(Block parent, int ntxs) {
        return createChildBlock(parent, ntxs, ByteUtil.bytesToBigInteger(parent.getDifficulty()).longValue());
    }

    public static Block createChildBlock(Block parent, int ntxs, long difficulty) {
        List<Transaction> txs = new ArrayList<>();

        for (int ntx = 0; ntx < ntxs; ntx++)
            txs.add(new SimpleRskTransaction(null));

        List<BlockHeader> uncles = new ArrayList<>();

        return createChildBlock(parent, txs, uncles, difficulty, null);
    }

    public static Block createChildBlock(Block parent, List<Transaction> txs) {
        return createChildBlock(parent, txs, new ArrayList<>(),
                ByteUtil.bytesToBigInteger(parent.getDifficulty()).longValue(), null);
    }

    public static Block createChildBlock(Block parent, List<Transaction> txs, List<BlockHeader> uncles,
            long difficulty, BigInteger minGasPrice) {
        if (txs == null)
            txs = new ArrayList<>();
        if (uncles == null)
            uncles = new ArrayList<>();

        Bloom logBloom = new Bloom();
        byte[] bidiff = BigInteger.valueOf(difficulty).toByteArray();
        byte[] unclesListHash = HashUtil.sha3(BlockHeader.getUnclesEncodedEx(uncles));

        BlockHeader newHeader = new BlockHeader(parent.getHash(), unclesListHash, parent.getCoinbase(),
                ByteUtils.clone(new Bloom().getData()), new byte[] { 1 }, parent.getNumber() + 1,
                parent.getGasLimit(), 0, parent.getTimestamp() + ++count, new byte[] {}, new byte[] {},
                new byte[] {}, new byte[] {}, (minGasPrice != null) ? minGasPrice.toByteArray() : null,
                CollectionUtils.size(uncles));

        if (difficulty == 0)
            newHeader.setDifficulty(newHeader.calcDifficulty(parent.getHeader()).toByteArray());
        else
            newHeader.setDifficulty(BigInteger.valueOf(difficulty).toByteArray());

        newHeader.setTransactionsRoot(Block.getTxTrie(txs).getHash());

        newHeader.setStateRoot(parent.getStateRoot());

        Block newBlock = new Block(newHeader, txs, uncles);

        return newBlock;
    }

    public static Block createBlock(int number, int ntxs) {
        Bloom logBloom = new Bloom();
        Block parent = BlockGenerator.getGenesisBlock();

        List<Transaction> txs = new ArrayList<>();

        for (int ntx = 0; ntx < ntxs; ntx++)
            txs.add(new SimpleRskTransaction(null));

        byte[] parentMGP = (parent.getMinimumGasPrice() != null) ? parent.getMinimumGasPrice()
                : BigInteger.valueOf(10L).toByteArray();
        BigInteger minimumGasPrice = new MinimumGasPriceCalculator().calculate(new BigInteger(1, parentMGP),
                BigInteger.valueOf(100L));

        return new Block(parent.getHash(), // parent hash
                EMPTY_LIST_HASH, // uncle hash
                parent.getCoinbase(), // coinbase
                logBloom.getData(), // logs bloom
                parent.getDifficulty(), // difficulty
                number, parent.getGasLimit(), parent.getGasUsed(), parent.getTimestamp() + ++count,
                EMPTY_BYTE_ARRAY, // extraData
                EMPTY_BYTE_ARRAY, // mixHash
                BigInteger.ZERO.toByteArray(), // provisory nonce
                EMPTY_TRIE_HASH, // receipts root
                EMPTY_TRIE_HASH, // transaction receipts
                EMPTY_TRIE_HASH, // state root
                txs, // transaction list
                null, // uncle list
                minimumGasPrice.toByteArray());
    }

    public static Block createEmptyGenesisBlock() {
        Bloom logBloom = new Bloom();
        Block original = BlockGenerator.getGenesisBlock();

        return new Block(original.getParentHash(), // parent hash
                EMPTY_LIST_HASH, // uncle hash
                original.getCoinbase(), // coinbase
                logBloom.getData(), // logs bloom
                original.getDifficulty(), // difficulty
                0, original.getGasLimit(), original.getGasUsed(), original.getTimestamp() + ++count,
                EMPTY_BYTE_ARRAY, // extraData
                EMPTY_BYTE_ARRAY, // mixHash
                BigInteger.ZERO.toByteArray(), // provisory nonce
                EMPTY_TRIE_HASH, // receipts root
                EMPTY_TRIE_HASH, // transaction receipts
                EMPTY_TRIE_HASH, // state root
                null, // transaction list
                null, // uncle list
                BigInteger.valueOf(RskSystemProperties.RSKCONFIG.minerMinGasPrice()).toByteArray());
    }

    public static Block createSimpleChildBlock(Block parent, int ntxs) {
        Bloom logBloom = new Bloom();

        List<Transaction> txs = new ArrayList<>();

        for (int ntx = 0; ntx < ntxs; ntx++)
            txs.add(new SimpleRskTransaction(PegTestUtils.createHash3().getBytes()));

        return new SimpleBlock(parent.getHash(), // parent hash
                EMPTY_LIST_HASH, // uncle hash
                parent.getCoinbase(), // coinbase
                logBloom.getData(), // logs bloom
                parent.getDifficulty(), // difficulty
                parent.getNumber() + 1, parent.getGasLimit(), parent.getGasUsed(), parent.getTimestamp() + ++count,
                EMPTY_BYTE_ARRAY, // extraData
                EMPTY_BYTE_ARRAY, // mixHash
                BigInteger.ZERO.toByteArray(), // provisory nonce
                EMPTY_TRIE_HASH, // receipts root
                EMPTY_TRIE_HASH, // transaction receipts
                EMPTY_TRIE_HASH, // state root
                txs, // transaction list
                null // uncle list
        );
    }

    public static List<Block> getBlockChain(int size) {
        return getBlockChain(BlockGenerator.getGenesisBlock(), size);
    }

    public static List<Block> getBlockChain(Block parent, int size) {
        return getBlockChain(parent, size, 0);
    }

    public static List<Block> getSimpleBlockChain(Block parent, int size) {
        return getSimpleBlockChain(parent, size, 0);
    }

    public static List<Block> getBlockChain(Block parent, int size, int ntxs) {
        return getBlockChain(parent, size, ntxs, false);
    }

    public static List<Block> getBlockChain(Block parent, int size, int ntxs, boolean withUncles) {
        List<Block> chain = new ArrayList<Block>();
        List<BlockHeader> uncles = new ArrayList<>();
        int chainSize = 0;

        while (chainSize < size) {
            List<Transaction> txs = new ArrayList<>();

            for (int ntx = 0; ntx < ntxs; ntx++)
                txs.add(new SimpleRskTransaction(null));

            Block newblock = BlockGenerator.createChildBlock(parent, txs, uncles,
                    ByteUtil.bytesToBigInteger(parent.getDifficulty()).longValue(), null);
            chain.add(newblock);

            if (withUncles) {
                uncles = new ArrayList<>();

                Block newuncle = BlockGenerator.createChildBlock(parent, ntxs);
                chain.add(newuncle);
                uncles.add(newuncle.getHeader());

                newuncle = BlockGenerator.createChildBlock(parent, ntxs);
                chain.add(newuncle);
                uncles.add(newuncle.getHeader());
            }

            parent = newblock;
            chainSize++;
        }

        return chain;
    }

    public static List<Block> getSimpleBlockChain(Block parent, int size, int ntxs) {
        List<Block> chain = new ArrayList<Block>();

        while (chain.size() < size) {
            Block newblock = BlockGenerator.createSimpleChildBlock(parent, ntxs);
            chain.add(newblock);
            parent = newblock;
        }

        return chain;
    }
}