Example #1
0
        public static byte[] ToHashBytes(this Block block, IHashProvider hashProvider)
        {
            foreach (var @object in block?.Objects ?? Enumerable.Empty <BlockObject>())
            {
                @object.Hash = hashProvider.ComputeHashBytes(@object);
            }

            return(hashProvider.ComputeHashBytes(block));
        }
 public Task <CurrencyBlock> GetGenesisBlockAsync()
 {
     foreach (var transaction in _coinSettings.Value.GenesisBlock.Transactions ?? Enumerable.Empty <Transaction>())
     {
         transaction.Hash = _hashProvider.ComputeHashBytes(transaction);
     }
     _coinSettings.Value.GenesisBlock.Hash = _coinSettings.Value.GenesisBlock.ToHashBytes(_hashProvider);
     return(Task.FromResult(_coinSettings.Value.GenesisBlock));
 }
        private void HashBlock(Block block)
        {
            var transactions = ((CurrencyBlock)block).Transactions;

            foreach (var transaction in transactions)
            {
                transaction.Hash = _hashProvider.ComputeHashBytes(transaction);
            }
            block.Hash = block.ToHashBytes(_hashProvider);
        }
Example #4
0
        public void Check(IHashProvider hashProvider, CoinSettings coinSettings)
        {
            // Check if the transaction hash is correct
            if (Hash != hashProvider.ComputeHashBytes(this))
            {
                throw new TransactionAssertionException($"Invalid transaction hash '{Hash}'");
            }

            Data.Check(coinSettings, hashProvider);
        }
Example #5
0
        public static byte[] ComputeMerkleRoot(this Block block, IHashProvider hashProvider)
        {
            if (block.Objects == null || block.Objects.Count == 0)
            {
                return(hashProvider.ComputeHashBytes(Block.NoObjects));
            }

            // https://en.bitcoin.it/wiki/Protocol_documentation#Merkle_Trees

            var p = new List <byte[]>();

            foreach (var o in block.Objects)
            {
                p.Add(hashProvider.DoubleHash(o));
            }
            if (p.Count > 1 && p.Count % 2 != 0)
            {
                p.Add(p[p.Count - 1]);
            }
            if (p.Count == 1)
            {
                return(p[0]);
            }

pass:
            {
                var n = new List <byte[]>(p.Count / 2);
                for (var i = 0; i < p.Count; i++)
                {
                    for (var j = i + 1; j < p.Count; j++)
                    {
                        var a = block.Objects[i].Hash;
                        var b = block.Objects[j].Hash;
                        var d = hashProvider.DoubleHash(a, b);
                        n.Add(d);
                        i++;
                    }
                }
                if (n.Count == 1)
                {
                    return(n[0]);
                }
                if (n.Count % 2 != 0)
                {
                    n.Add(n[n.Count - 1]);
                }

                p = n;
                goto pass;
            }
        }
Example #6
0
        public void Check(CoinSettings coinSettings, IHashProvider hashProvider)
        {
            // Check if the signature of all input transactions are correct (transaction data is signed by the public key of the address)
            var checks = Inputs.Select(input =>
            {
                var hash = hashProvider.ComputeHashBytes(new
                {
                    Transaction = input.TransactionId,
                    input.Index,
                    input.Address
                });

                return(new
                {
                    valid = Ed25519.VerifySignature(input.Address, input.Signature, hash),
                    input
                });
            });

            foreach (var check in checks)
            {
                if (!check.valid)
                {
                    throw new TransactionAssertionException($"Invalid transaction input signature at index '{check.input.Index}'");
                }
            }

            // Check if the sum of input transactions are greater than output transactions, it needs to leave some room for the transaction fee
            var sumOfInputsAmount  = Inputs.Sum(x => x.Amount);
            var sumOfOutputsAmount = Outputs.Sum(x => x.Amount);

            if (sumOfInputsAmount >= sumOfOutputsAmount)
            {
                throw new TransactionAssertionException($"Invalid transaction balance: inputs sum '{sumOfInputsAmount}', outputs sum '{sumOfOutputsAmount}'");
            }

            // Check if there is enough to cover the transaction fee
            var isEnoughFee = sumOfInputsAmount - sumOfOutputsAmount >= coinSettings.FeePerTransaction;

            if (!isEnoughFee)
            {
                throw new TransactionAssertionException($"Not enough fee: expected '{coinSettings.FeePerTransaction}' got '{sumOfInputsAmount - sumOfOutputsAmount}'");
            }
        }
Example #7
0
        public Transaction Build()
        {
            // Check required information
            if (_utxo == null)
            {
                throw new ArgumentException($"It's necessary to provide a list of unspent output transactions.");
            }
            if (_outputAddress == null)
            {
                throw new ArgumentException($"It's necessary to provide the destination address.");
            }
            if (_totalAmount == null)
            {
                throw new ArgumentException($"It's necessary to provide the transaction value.");
            }

            // Calculates the change amount
            var changeAmount = _utxo.Sum(x => x.Amount) - _totalAmount - _feeAmount;

            var transactionId = CryptoUtil.RandomString();

            // For each transaction input, calculates the hash of the input and signs the data
            var inputIndex = 1;
            var inputs     = _utxo.Select(utxo =>
            {
                var keyPair = Ed25519.GenerateKeyPairFromSecret(_secretKey);
                var hash    = _hashProvider.ComputeHashString(new
                {
                    Transaction = utxo.TransactionId,
                    utxo.Index,
                    utxo.Address
                });
                utxo.Signature = Ed25519.Sign(keyPair, hash);
                return(utxo);
            }).Select(x => new TransactionItem
            {
                TransactionId = x.TransactionId,
                Index         = x.Index,
                Address       = x.Address,
                Amount        = x.Amount,
                Signature     = x.Signature,
                Type          = TransactionDataType.Input
            }).AsList();

            // Add target receiver
            var outputIndex = 1;
            var outputs     = new List <TransactionItem>
            {
                new TransactionItem
                {
                    Index         = outputIndex++,
                    TransactionId = transactionId,
                    Amount        = _totalAmount.Value,
                    Address       = _outputAddress,
                    Type          = TransactionDataType.Output
                }
            };

            // Add change amount
            if (changeAmount > 0)
            {
                outputs.Add(new TransactionItem
                {
                    Index         = outputIndex,
                    TransactionId = transactionId,
                    Amount        = changeAmount.GetValueOrDefault(),
                    Address       = _changeAddress,
                    Type          = TransactionDataType.Output
                });
            }

            // The remaining value is the fee to be collected by the block's creator
            var transaction = new Transaction
            {
                Id   = transactionId,
                Type = _type,
                Data = new TransactionData
                {
                    Inputs  = inputs,
                    Outputs = outputs.AsList()
                }
            };

            transaction.Hash = _hashProvider.ComputeHashBytes(transaction);

            return(transaction);
        }
Example #8
0
 public static byte[] ToHashBytes(this Block block, IHashProvider hashProvider)
 {
     return(hashProvider.ComputeHashBytes(block));
 }
Example #9
0
 public static byte[] ToHashBytes(this IBlockSerialized data, IHashProvider hashProvider)
 {
     return(hashProvider.ComputeHashBytes(data));
 }