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); }
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); }
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; } }
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}'"); } }
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); }
public static byte[] ToHashBytes(this Block block, IHashProvider hashProvider) { return(hashProvider.ComputeHashBytes(block)); }
public static byte[] ToHashBytes(this IBlockSerialized data, IHashProvider hashProvider) { return(hashProvider.ComputeHashBytes(data)); }