Example #1
0
        private async Task <Chain> ProcessBlock(Block block)
        {
            Log.Debug("Starting process block");
            if (Previous == null && CurrentBlock == null && block.Header.PreviousBlockHash == null)
            {
                Log.Debug("Creating genesis chain");
                CancelToken.Cancel();
                Mining = false;
                Log.Debug("Genesis chain pool created");

                if (block.Transactions.Count() != 1 ||
                    block.Transactions.First().Outputs[0].Wallet != "y4j4EmPLy7oMBeQEdfXJJAqwWdwgpPGAxezDWNGzFqqtqjKpn" ||  //TODO Changel PreMining Wallet
                    block.Transactions.First().Outputs[0].Size != 1578960000)
                {
                    Log.Error("Genesis block with invalid transaction");
                    return(null);
                }

                var tx = TransactManager.ProcessTransaction(block.Transactions.First());
                return(new Chain(this, block, Pool, tx, threads));
            }

            Log.Debug("Certifying dificulty");

            var lastDeth = this.GetLastBlockDepth();

            if (CurrentBlock != null && (CurrentBlock.Header.Depth - 20) >= lastDeth && block.Header.Difficult != GetTargetDiff())
            {
                Log.Error("Invalid Block Difficulty");
                return(null);
            }

            Log.Debug("Verifying Depth");

            if (CurrentBlock != null && block.Header.Depth != CurrentBlock.Header.Depth + 1)
            {
                Log.Error("Invalid Block Depth");
                Log.Error("Current Block Depth " + CurrentBlock.Header.Depth);
                Log.Error("Current Block Hash " + CurrentBlock.HashStr);
                Log.Error("Received Block Depth " + block.Header.Depth);
                Log.Error("Received Block Previous Hash " + block.PreviousHashStr);
                return(null);
            }

            Log.Debug("Verifying previous hash null");
            if (block.Header.PreviousBlockHash == null)
            {
                Log.Error("Received Empty previous block hash after genesis");
                return(null);
            }

            Log.Debug("Verifying previous block hash match");
            if (CurrentBlock != null && !block.Header.PreviousBlockHash.SequenceEqual(CurrentBlock.Header.Hash))
            {
                Log.Error("Invalid Previous Block Hash");
                Log.Error("PreviusBlockHash " + Base58.Bitcoin.Encode(new Span <Byte>(block.Header.PreviousBlockHash)));
                Log.Error("LastBlockHash " + Base58.Bitcoin.Encode(new Span <Byte>(CurrentBlock.Header.Hash)));

                return(null);
            }

            if (block.Timestamp < CurrentBlock.Timestamp || block.Timestamp > DateTime.Now.ToUniversalTime())
            {
                Log.Error("Block with invalid timestamp");
                return(null);
            }

            Log.Debug("Block Header validated.");

            var transact = TransactManager;
            var result   = false;

            byte[] root = new byte[0];
            Log.Debug("Validatig deposits");

            var deposit = new Immutable.Deposit(DepositManager, transact);

            if (block.Deposits != null && block.DepositsDictionary.Count > 0)
            {
                foreach (var dp in block.Deposits)
                {
                    if (CancelToken.IsCancellationRequested)
                    {
                        return(null);
                    }

                    if (!await Pool.Contains(dp))
                    {
                        Log.Error("Block with invalid deposit");
                        return(null);
                    }

                    deposit = deposit.ProcessDeposit(dp);
                    root    = CryptoHelper.Hash(Base58.Bitcoin.Encode(new Span <Byte>(root)) + dp.ToString());
                }
            }
            transact = deposit.TransactManager;

            Log.Debug("Deposits validated");

            Log.Debug("Validating book");
            var book   = new Book(BookManager, transact);
            var trades = new List <Trade>();

            if (block.OffersDictionary.Count > 0)
            {
                foreach (var of in block.Offers)
                {
                    if (CancelToken.IsCancellationRequested)
                    {
                        return(null);
                    }

                    if (!await Pool.Contains(of))
                    {
                        Log.Error("Block with invalid offer");
                        return(null);
                    }

                    var clone = of.Clone();
                    clone.CleanTrades();

                    (result, book) = book.ProcessOffer(clone);

                    if (!result)
                    {
                        Log.Error("Block with invalid offer");
                        return(null);
                    }

                    foreach (var t in of.Trades)
                    {
                        if (!clone.Trades.Any(c => c.Equals(t)))
                        {
                            Log.Error("Block with invalid trade");
                            return(null);
                        }

                        root = CryptoHelper.Hash(Base58.Bitcoin.Encode(new Span <Byte>(root)) + t.ToString());
                    }

                    clone.Trades = of.Trades;
                    trades.AddRange(of.Trades);

                    root = CryptoHelper.Hash(Base58.Bitcoin.Encode(new Span <Byte>(root)) + clone.ToString());
                }
            }

            book.Trades = trades.ToImmutableList();
            transact    = book.TransactManager;

            if (block.OffersCancelDictionary.Count > 0)
            {
                foreach (var of in block.OfferCancels)
                {
                    if (CancelToken.IsCancellationRequested)
                    {
                        return(null);
                    }

                    if (!await Pool.Contains(of))
                    {
                        Log.Error("Block with invalid offer cancel");
                        return(null);
                    }

                    var clone = of.Clone();

                    (result, book) = book.ProcessOfferCancel(clone);

                    if (!result)
                    {
                        Log.Error("Block with invalid offer cancel");
                        return(null);
                    }

                    root = CryptoHelper.Hash(Base58.Bitcoin.Encode(new Span <Byte>(root)) + clone.ToString());
                }
            }
            transact = book.TransactManager;
            Log.Debug("Book validated, validating transactions");
            (result, transact, root) = await ValidateTransactions(root, block, transact);

            if (!result)
            {
                return(null);
            }

            Log.Debug("Transactions validated, validating withdrawals");

            var withdrawal = new Immutable.Withdrawal(WithdrawalManager, transact);

            if (block.Withdrawals != null && block.WithdrawalsDictionary.Count > 0)
            {
                foreach (var wd in block.Withdrawals)
                {
                    if (CancelToken.IsCancellationRequested)
                    {
                        return(null);
                    }

                    if (!await Pool.Contains(wd) && !withdrawal.CanProcess(wd))
                    {
                        Log.Error("Block with invalid withdrawal.");
                        return(null);
                    }

                    withdrawal = withdrawal.ProcessWithdrawal(wd);
                    root       = CryptoHelper.Hash(Base58.Bitcoin.Encode(new Span <Byte>(root)) + wd.ToString());
                }
            }

            transact = withdrawal.TransactManager;

            Log.Debug("withdrawals validated");

            if (!block.Header.MerkleRoot.SequenceEqual(root))
            {
                Log.Error("Block with invalid merkle root");
                return(null);
            }

            Log.Debug("Merkle root validated, getting commit lock");

            try
            {
                await commitLocker.WaitAsync();

                Log.Debug("commit lock gained");
                if (!CancelToken.IsCancellationRequested)
                {
                    CancelToken.Cancel();
                    Log.Debug("Block references last block, appending");
                    Mining = false;
                    transact.Balance.BlockHash = block.HashStr;
                    transact.Balance.Timestamp = block.Timestamp;
                    book.BlockHash             = block.HashStr;
                    book.Timestamp             = block.Timestamp;
                    return(new Chain(this, transact, deposit, withdrawal, book, block, CurrentBlock, Pool, threads));
                }
            }
            finally
            {
                commitLocker.Release();
            }

            return(null);
        }
Example #2
0
        public async Task <Chain> StartMining()
        {
            try
            {
                await commitLocker.WaitAsync();

                if (!CancelToken.IsCancellationRequested && !Mining)
                {
                    Mining = true;
                }
                else
                {
                    Log.Warning("Already Mining...");
                    return(null);
                }
            }
            finally
            {
                commitLocker.Release();
            }

            Mining = true;

            if (CurrentBlock == null)
            {
                var genesis = await Genesis();

                if (genesis != null)
                {
                    var tx = TransactManager.ProcessTransaction(genesis.Transactions.First());
                    return(new Chain(this, genesis, Pool, tx, threads));
                }
            }
            else
            {
                var block = new Block();
                block.Header.Depth             = CurrentBlock.Header.Depth + 1;
                block.Header.PreviousBlockHash = CurrentBlock.Header.Hash;
                block.Header.Difficult         = GetTargetDiff();

                byte[] root = new byte[0];

                var            deposit           = new Immutable.Deposit(DepositManager, TransactManager);
                List <Deposit> ellegibleDeposits = null;
                (deposit, ellegibleDeposits, root) = deposit.ProcessDeposits(root, await GetDepositPool());
                var transact = deposit.TransactManager;
                block.Deposits = ellegibleDeposits;

                var book            = new Book(BookManager, transact);
                var ellegibleOffers = new List <Offer>();
                (book, ellegibleOffers, root) = book.ProcessOffers(root, await GetOfferPool());
                transact     = book.TransactManager;
                block.Offers = ellegibleOffers;

                var ellegibleOfferCancels = new List <OfferCancel>();
                (book, ellegibleOfferCancels, root) = book.ProcessOfferCancels(root, await GetOfferCancelPool());
                transact           = book.TransactManager;
                block.OfferCancels = ellegibleOfferCancels;

                List <Transaction> ellegibleTransactions = null;
                (transact, ellegibleTransactions, root) = transact.ProcessTransactions(root, minerWallet, await GetTransactionPool(), CancelToken);
                block.Transactions = ellegibleTransactions;

                if (CancelToken.IsCancellationRequested)
                {
                    return(null);
                }

                var withdrawal = new Immutable.Withdrawal(WithdrawalManager, transact);
                List <Withdrawal> ellegibleWithdrawals = null;
                (withdrawal, ellegibleWithdrawals, root) = withdrawal.ProcessWithdrawals(root, await GetWithdrawalPool());
                transact          = withdrawal.TransactManager;
                block.Withdrawals = ellegibleWithdrawals;

                block.Offers = ellegibleOffers;
                Log.Information("Mining New Block");
                Log.Information(ellegibleTransactions.Count + " transactions");
                Log.Information(ellegibleDeposits.Count + " deposits");
                Log.Information(ellegibleWithdrawals.Count + " withdrawals");
                Log.Information(ellegibleOffers.Count + " offers");
                block.Header.MerkleRoot = root;
                block.Header.TimeStamp  = DateTime.Now;
                var result = await Mine(block, CancelToken);

                if (result != null)
                {
                    Log.Debug("Getting commit lock");
                    try
                    {
                        await commitLocker.WaitAsync();

                        if (!CancelToken.IsCancellationRequested)
                        {
                            Log.Debug("Got commit lock");
                            CancelToken.Cancel();
                            Mining = false;
                            Mined  = true;
                            transact.Balance.BlockHash = result.HashStr;
                            transact.Balance.Timestamp = result.Timestamp;
                            book.BlockHash             = result.HashStr;
                            book.Timestamp             = result.Timestamp;
                            return(new Chain(this, transact, deposit, withdrawal, book, result, CurrentBlock, Pool, threads));
                        }
                    }
                    finally
                    {
                        commitLocker.Release();
                    }
                }
            }

            return(await Task <Chain> .FromResult((Chain)null));
        }