Exemplo n.º 1
0
        /// <summary>
        /// Mines a new genesis block, to use with a new network.
        /// Typically, 3 such genesis blocks need to be created when bootstrapping a new coin: for Main, Test and Reg networks.
        /// </summary>
        /// <param name="consensusFactory">
        /// The consensus factory used to create transactions and blocks.
        /// Use <see cref="PosConsensusFactory"/> for proof-of-stake based networks.
        /// </param>
        /// <param name="coinbaseText">
        /// Traditionally a news headline from the day of the launch, but could be any string or link.
        /// This will be inserted in the input coinbase transaction script.
        /// It should be shorter than 92 characters.
        /// </param>
        /// <param name="target">
        /// The difficulty target under which the hash of the block need to be.
        /// Some more details: As an example, the target for the Stratis Main network is 00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff.
        /// To make it harder to mine the genesis block, have more zeros at the beginning (keeping the length the same). This will make the target smaller, so finding a number under it will be more difficult.
        /// To make it easier to mine the genesis block ,do the opposite. Example of an easy one: 00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff.
        /// Make the Test and Reg targets ones easier to find than the Main one so that you don't wait too long to mine the genesis block.
        /// </param>
        /// <param name="genesisReward">
        /// Specify how many coins to put in the genesis transaction's output. These coins are unspendable.
        /// </param>
        /// <param name="version">
        /// The version of the transaction and the block header set in the genesis block.
        /// </param>
        /// <example>
        /// The following example shows the creation of a genesis block.
        /// <code>
        /// Block genesis = MineGenesisBlock(new PosConsensusFactory(), "Some topical headline.", new Target(new uint256("000fffff00000000000000000000000000000000000000000000000000000000")), Money.Coins(50m));
        /// BlockHeader header = genesis.Header;
        /// Console.WriteLine("Make a note of the following values:");
        /// Console.WriteLine("bits: " + header.Bits);
        /// Console.WriteLine("nonce: " + header.Nonce);
        /// Console.WriteLine("time: " + header.Time);
        /// Console.WriteLine("version: " + header.Version);
        /// Console.WriteLine("hash: " + header.GetHash());
        /// Console.WriteLine("merkleroot: " + header.HashMerkleRoot);
        /// </code>
        /// </example>
        /// <returns>A genesis block.</returns>
        public static Block MineGenesisBlock(ConsensusFactory consensusFactory, string coinbaseText, Target target, Money genesisReward, int version = 1)
        {
            if (consensusFactory == null)
                throw new ArgumentException($"Parameter '{nameof(consensusFactory)}' cannot be null. Use 'new ConsensusFactory()' for Bitcoin-like proof-of-work blockchains and 'new PosConsensusFactory()' for Stratis-like proof-of-stake blockchains.");

            if (string.IsNullOrEmpty(coinbaseText))
                throw new ArgumentException($"Parameter '{nameof(coinbaseText)}' cannot be null. Use a news headline or any other appropriate string.");

            if (target == null)
                throw new ArgumentException($"Parameter '{nameof(target)}' cannot be null. Example use: new Target(new uint256(\"0000ffff00000000000000000000000000000000000000000000000000000000\"))");

            if (coinbaseText.Length >= 92)
                throw new ArgumentException($"Parameter '{nameof(coinbaseText)}' should be shorter than 92 characters.");

            if (genesisReward == null)
                throw new ArgumentException($"Parameter '{nameof(genesisReward)}' cannot be null. Example use: 'Money.Coins(50m)'.");

            DateTimeOffset time = DateTimeOffset.Now;
            uint unixTime = Utils.DateTimeToUnixTime(time);

            Transaction txNew = consensusFactory.CreateTransaction();
            txNew.Version = (uint)version;
            txNew.AddInput(new TxIn()
            {
                ScriptSig = new Script(
                    Op.GetPushOp(0),
                    new Op()
                    {
                        Code = (OpcodeType)0x1,
                        PushData = new[] { (byte)42 }
                    },
                    Op.GetPushOp(Encoders.ASCII.DecodeData(coinbaseText)))
            });

            txNew.AddOutput(new TxOut()
            {
                Value = genesisReward,
            });

            Block genesis = consensusFactory.CreateBlock();
            genesis.Header.BlockTime = time;
            genesis.Header.Bits = target;
            genesis.Header.Nonce = 0;
            genesis.Header.Version = version;
            genesis.Transactions.Add(txNew);
            genesis.Header.HashPrevBlock = uint256.Zero;
            genesis.UpdateMerkleRoot();

            // Iterate over the nonce until the proof-of-work is valid.
            // This will mean the block header hash is under the target.
            while (!genesis.CheckProofOfWork())
            {
                genesis.Header.Nonce++;
                if (genesis.Header.Nonce == 0)
                    genesis.Header.Time++;
            }

            return genesis;
        }
Exemplo n.º 2
0
        // a method to check a block, this may be moved to the full node.
        public static bool CheckBlock(Block block, bool checkPow = true, bool checkMerkleRoot = true, bool checkSig = true)
        {
            // These are checks that are independent of context
            // that can be verified before saving an orphan block.

            // Size limits
            if (!block.Transactions.Any() || block.GetSerializedSize() > MAX_BLOCK_SIZE)
            {
                return(false);                // DoS(100, error("CheckBlock() : size limits failed"));
            }
            // Check proof of work matches claimed amount
            if (checkPow && BlockStake.IsProofOfWork(block) && !block.CheckProofOfWork())
            {
                return(false);                //DoS(50, error("CheckBlock() : proof of work failed"));
            }
            // Check timestamp
            if (block.Header.Time > FutureDriftV2(DateTime.UtcNow.Ticks)) //GetAdjustedTime()))
            {
                return(false);                                            //error("CheckBlock() : block timestamp too far in he future");
            }
            // First transaction must be coinbase, the rest must not be
            if (!block.Transactions[0].IsCoinBase)
            {
                return(false);                //  DoS(100, error("CheckBlock() : first tx is not coinbase"));
            }
            if (block.Transactions.Skip(1).Any(t => t.IsCoinBase))
            {
                return(false);                //DoS(100, error("CheckBlock() : more than one coinbase"));
            }
            if (BlockStake.IsProofOfStake(block))
            {
                // Coinbase output should be empty if proof-of-stake block
                if (block.Transactions[0].Outputs.Count != 1 || !block.Transactions[0].Outputs[0].IsEmpty)
                {
                    return(false);                    // DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block"));
                }
                // Second transaction must be coinstake, the rest must not be
                if (!block.Transactions[1].IsCoinStake)
                {
                    return(false);                    // DoS(100, error("CheckBlock() : second tx is not coinstake"));
                }
                if (block.Transactions.Skip(2).Any(t => t.IsCoinStake))
                {
                    return(false);                    //DoS(100, error("CheckBlock() : more than one coinstake"));
                }
            }

            // Check proof-of-stake block signature
            if (checkSig && !CheckBlockSignature(block))
            {
                return(false);                //DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));
            }
            // Check transactions
            foreach (var transaction in block.Transactions)
            {
                if (transaction.Check() != TransactionCheckResult.Success)
                {
                    return(false);                    // DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
                }
                // ppcoin: check transaction timestamp
                if (block.Header.Time < transaction.Time)
                {
                    return(false);                    // DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
                }
            }

            // Check for duplicate txids. This is caught by ConnectInputs(),
            // but catching it earlier avoids a potential DoS attack:
            var set = new HashSet <uint256>();

            if (block.Transactions.Select(t => t.GetHash()).Any(h => !set.Add(h)))
            {
                return(false);                //DoS(100, error("CheckBlock() : duplicate transaction"));
            }
            // todo: check if this is legacy from older implementtions and actually needed
            //uint nSigOps = 0;
            //foreach (var transaction in block.Transactions)
            //{
            //	nSigOps += GetLegacySigOpCount(transaction);
            //}
            //if (nSigOps > MAX_BLOCK_SIGOPS)
            //	return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));

            // Check merkle root
            if (checkMerkleRoot && !block.CheckMerkleRoot())
            {
                return(false);                //DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
            }
            return(true);
        }