コード例 #1
0
        public Block MineNextBlock(string minerAddress, int difficulty)
        {
            // Prepare the next block for mining
            var oldDifficulty = this.CurrentDifficulty;

            this.CurrentDifficulty = difficulty;
            var nextBlock = this.GetMiningJob(minerAddress);

            this.CurrentDifficulty = oldDifficulty;

            // Mine the next block
            nextBlock.DateCreated = GeneralUtils.NowInISO8601();
            nextBlock.Nonce       = 0;
            do
            {
                nextBlock.Nonce++;
                nextBlock.BlockHash = nextBlock.CalculateBlockHash();
            } while (!ValidationUtils.IsValidDifficulty(CryptoUtils.BytesToHex(nextBlock.BlockHash), difficulty));

            // Submit the mined block
            var newBlock = this.SubmitMinedBlock(nextBlock.BlockDataHash, // ?? thing if I have to recalculate that
                                                 nextBlock.DateCreated, nextBlock.Nonce, nextBlock.BlockHash);

            return(newBlock);
        }
コード例 #2
0
        public Block ExtendChain(Block newBlock)
        {
            if (newBlock.Index != this.Blocks.Count)
            {
                throw new ArgumentException("The submitted block was already mined by someone else");
            }


            var prevBlock = this.Blocks[this.Blocks.Count - 1];

            if (CryptoUtils.BytesToHex(prevBlock.BlockHash) != CryptoUtils.BytesToHex(newBlock.PrevBlockHash))
            {
                throw new ArgumentException("Incorrect prevBlockHash");
            }


            // The block is correct --> accept it
            this.Blocks.Add(newBlock);
            this.MiningJobs = new Dictionary <string, Block>(); // Invalidate all mining jobs
            this.RemovePendingTransactions(newBlock.Transactions);

            this.AdjustDifficulty();

            return(newBlock);
        }
コード例 #3
0
        public Transaction FindTransactionByDataHash(string transactionDataHash)
        {
            var allTransactions     = this.GetAllTransactions();
            var matchingTransaction = allTransactions.FirstOrDefault(
                t => CryptoUtils.BytesToHex(t.TransactionDataHash) == transactionDataHash);

            return(matchingTransaction);
        }
コード例 #4
0
 private Node(string serverHost, string serverPort, Blockchain blockchain)
 {
     this.NodeId  = Guid.NewGuid().ToString();
     this.Host    = serverHost;
     this.Port    = serverPort;
     this.SelfUrl = string.Format("http://{0}:{1}", serverHost, serverPort);
     this.Peers   = new Dictionary <string, string>();
     this.Chain   = blockchain;
     this.ChainId = CryptoUtils.BytesToHex(blockchain.Blocks[0].BlockHash);
 }
コード例 #5
0
        public void Test_CryptoUtils_HexToBytesAndViseVersa()
        {
            var expResult = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08";
            var bytes     = CryptoUtils.HexToBytes(expResult);

            Assert.True(bytes.Length == 32);
            var resultString = CryptoUtils.BytesToHex(bytes);

            Assert.True(expResult == resultString);
            Assert.True(expResult.Length == 64);
        }
コード例 #6
0
        public void RemovePendingTransactions(IEnumerable <Transaction> transactionsToRemove)
        {
            var tranHashesToRemove = new HashSet <string>();

            foreach (var item in transactionsToRemove)
            {
                tranHashesToRemove.Add(CryptoUtils.BytesToHex(item.TransactionDataHash));
            }

            foreach (var dataHash in tranHashesToRemove)
            {
                this.PendingTransactions.RemoveAll(s => CryptoUtils.BytesToHex(s.TransactionDataHash) == dataHash);
            }
        }
コード例 #7
0
        public IActionResult GetMiningJob(string address)
        {
            var blockCandidate = this.GetNodeSingleton().Chain.GetMiningJob(address);

            //retun candidate block
            return(Ok(new
            {
                index = blockCandidate.Index,
                transactionsIncluded = blockCandidate.Transactions.Count,
                difficulty = blockCandidate.Difficulty,
                expectedReward = blockCandidate.Transactions[0].Value,
                rewardAddress = blockCandidate.Transactions[0].To,
                blockDataHash = CryptoUtils.BytesToHex(blockCandidate.BlockDataHash)
            }));
        }
コード例 #8
0
        }                                    // the unique chain ID (hash of the genesis block)


        public void BroadcastTransactionToAllPeers(Transaction tran)
        {
            foreach (var nodeId in this.Peers)
            {
                var peerUrl = this.Peers[nodeId.Key];

                Console.WriteLine("Broadcasting a transaction {0} to peer {1}"
                                  , CryptoUtils.BytesToHex(tran.TransactionDataHash), peerUrl);

                try
                {
                    var result = WebRequester
                                 .Post(peerUrl + "/api/Node/transactions/send", tran);
                }
                catch (Exception ex)
                {
                    Console
                    .WriteLine("Broadcasting a transaction to {0} failed, due to {1}"
                               , peerUrl, ex.Message);
                }
            }
        }
コード例 #9
0
        public Block SubmitMinedBlock(byte[] blockDataHash, string dateCreated, ulong nonce, byte[] blockHash)
        {
            // Find the block candidate by its data hash
            var newBlock = this.MiningJobs[CryptoUtils.BytesToHex(blockDataHash)];

            if (newBlock == null)
            {
                throw new ArgumentException("Block not found or already mined");
            }


            // Build the new block
            newBlock.DateCreated = dateCreated;
            newBlock.Nonce       = nonce;

            newBlock.BlockHash = newBlock.CalculateBlockHash();

            // We can not compare block hash because the one we have in newBlock is not ok
            // Validate the block hash + the proof of work
            //if (CryptoUtils.BytesToHex(newBlock.BlockHash) != CryptoUtils.BytesToHex(blockHash))
            //    throw new ArgumentException("Block hash is incorrectly calculated");

            if (!ValidationUtils.IsValidDifficulty(
                    CryptoUtils.BytesToHex(newBlock.BlockHash), newBlock.Difficulty))
            {
                throw new ArgumentException("The calculated block hash does not match the block difficulty");
            }


            newBlock = this.ExtendChain(newBlock);

            //if (!newBlock.errorMsg)
            //    logger.debug("Mined a new block: " + JSON.stringify(newBlock));

            return(newBlock);
        }
コード例 #10
0
        public byte[] CalculateBlockHash()
        {
            var data = string.Format("{0}|{1}|{2}", CryptoUtils.BytesToHex(this.BlockDataHash), this.DateCreated, this.Nonce);

            return(CryptoUtils.CalcSHA256(data));
        }
コード例 #11
0
        public Block GetMiningJob(string minerAddress)
        {
            var nextBlockIndex = this.Blocks.Count;

            // Deep clone all pending transactions & sort them by fee
            var clonedTransactions = (SerializeUtils.CloneList <Transaction>(this.PendingTransactions))
                                     .OrderByDescending(s => s.Fee).ToList(); // sort descending by fee

            var dateCreated = GeneralUtils.NowInISO8601();
            var key         = nextBlockIndex + "_" + minerAddress;

            if (MinerAddressBlockIndexDateTimeMap.ContainsKey(key))
            {
                dateCreated = MinerAddressBlockIndexDateTimeMap[key];
            }
            else
            {
                MinerAddressBlockIndexDateTimeMap[key] = dateCreated;
            }

            // Prepare the coinbase transaction -> it will collect all tx fees
            var coinbaseTransaction = new Transaction(
                Config.NullAddress,           // from (address)
                minerAddress,                 // to (address)
                Config.BlockReward,           // value (of transfer)
                0,                            // fee (for mining)
                dateCreated,                  // dateCreated
                "coinbase tx",                // data (payload / comments)
                Config.NullPubKey,            // senderPubKey
                null,                         // transactionDataHash
                Config.GetNullSignatureHex(), // senderSignature
                nextBlockIndex,               // minedInBlockIndex
                true
                );

            // Execute all pending transactions (after paying their fees)
            // Transfer the requested values if the balance is sufficient
            var balances = this.CalcAllConfirmedBalances();

            foreach (var tran in clonedTransactions)
            {
                if (!balances.ContainsKey(tran.From))
                {
                    balances[tran.From] = 0;
                }

                if (!balances.ContainsKey(tran.To))
                {
                    balances[tran.To] = 0;
                }

                if (balances[tran.From] >= tran.Fee)
                {
                    tran.MinedInBlockIndex = nextBlockIndex;

                    // The transaction sender pays the processing fee
                    balances[tran.From]       -= tran.Fee;
                    coinbaseTransaction.Value += tran.Fee;

                    // Transfer the requested value: sender -> recipient
                    if (balances[tran.From] >= tran.Value)
                    {
                        balances[tran.From]    -= tran.Value;
                        balances[tran.To]      += tran.Value;
                        tran.TransferSuccessful = true;
                    }
                    else
                    {
                        tran.TransferSuccessful = false;
                    }
                }
                else
                {
                    // The transaction cannot be mined due to insufficient
                    // balance to pay the transaction fee -> drop it
                    this.RemovePendingTransactions(new List <Transaction>()
                    {
                        tran
                    });
                    clonedTransactions.Remove(tran);
                }
            }

            // Insert the coinbase transaction, holding the block reward + tx fees
            coinbaseTransaction.CalculateDataHash();
            clonedTransactions.Insert(0, coinbaseTransaction);

            // Prepare the next block candidate (block template)
            var previousBlock = this.Blocks[this.Blocks.Count - 1];

            //TODO: test that with Date null??
            var nextBlockCandidate = new Block(
                nextBlockIndex,
                clonedTransactions.ToList(),
                this.CurrentDifficulty,
                previousBlock.BlockHash,
                previousBlock.Index,
                minerAddress
                );

            this.MiningJobs[CryptoUtils.BytesToHex(nextBlockCandidate.BlockDataHash)] = nextBlockCandidate;
            return(nextBlockCandidate);
        }
コード例 #12
0
        public Transaction AddNewTransaction(Transaction tranData)
        {
            // Validate the transaction & add it to the pending transactions
            if (!ValidationUtils.IsValidAddress(tranData.From))
            {
                throw new ArgumentException("Invalid sender address:" + tranData.From);
            }

            if (!ValidationUtils.IsValidAddress(tranData.To))
            {
                throw new ArgumentException("Invalid recipient address:" + tranData.To);
            }

            if (!ValidationUtils.IsValidPublicKey(tranData.SenderPubKey))
            {
                throw new ArgumentException("Invalid public key:" + tranData.SenderPubKey);
            }

            var senderAddr = CryptoUtils.GetAddressFromPublicKey(tranData.SenderPubKey);

            if (senderAddr != tranData.From)
            {
                throw new ArgumentException("The public key should match the sender address:" + tranData.SenderPubKey);
            }

            if (!ValidationUtils.IsValidTransferValue(tranData.Value))
            {
                throw new ArgumentException("Invalid transfer value: " + tranData.Value);
            }

            if (!ValidationUtils.IsValidFee(tranData.Fee))
            {
                throw new ArgumentException("Invalid transaction fee: " + tranData.Fee);
            }

            if (!ValidationUtils.IsValidDate(tranData.DateCreated))
            {
                throw new ArgumentException("Invalid date: " + tranData.DateCreated);
            }

            if (!ValidationUtils.IsValidSignatureFormat(CryptoUtils.SignatureByHex(tranData.SenderSignature)))
            {
                throw new ArgumentException("Invalid or missing signature. Expected signature format: BigInteger[2] " + tranData.SenderSignature);
            }


            var tran = new Transaction(
                tranData.From,
                tranData.To,
                tranData.Value,
                tranData.Fee,
                tranData.DateCreated,
                tranData.Data,
                tranData.SenderPubKey,
                null, // the transactionDataHash is auto-calculated
                tranData.SenderSignature

                );

            //tran.IsSignatureValid = tranData.IsSignatureValid;

            // Check for duplicated transactions (to avoid "replay attack")
            var tranDataHex = CryptoUtils.BytesToHex(tran.TransactionDataHash);

            if (this.FindTransactionByDataHash(tranDataHex) != null)
            {
                throw new ArgumentException("Duplicated transaction: " + tranDataHex);
            }


            if (!CryptoUtils.VerifySignature(CryptoUtils.SignatureByHex(tran.SenderSignature), tran.TransactionDataHash))
            {
                throw new ArgumentException("Invalid signature: " + tranData.SenderSignature);
            }


            var balances = this.GetAccountBalance(tran.From);

            if (balances.ConfirmedBalance < tran.Fee)
            {
                throw new ArgumentException("Unsufficient sender balance at address:  " + tran.From);
            }

            //if (this.PendingTransactions.Any(s => CryptoUtils.BytesToHex(s.TransactionDataHash) == CryptoUtils.BytesToHex(tran.TransactionDataHash)))
            //    throw new ArgumentException("Trying to add duplicate transaction:  " + CryptoUtils.BytesToHex(tran.TransactionDataHash));

            this.PendingTransactions.Add(tran);
            Console.WriteLine("Added pending transaction: " + JsonConvert.SerializeObject(tran));

            return(tran);
        }