public static void VerifyTransaction(Transaction tx, DateTime timestamp, ulong coinbase = 0) { if (tx.TimeStamp > timestamp || !(coinbase == 0 ^ tx.Inputs.Count == 0)) { throw new ArgumentException(); } var hash = HashUtil.ComputeTransactionSignHash(JsonSerializer.Serialize(tx)); //Input check var inSum = coinbase; foreach (var input in tx.Inputs) { var chainTxs = Chain.SelectMany(x => x.Transactions); //Input Verify var transactions = chainTxs as Transaction[] ?? chainTxs.ToArray(); var prevOutTx = transactions .First(x => x.Id.Bytes == input.TransactionId.Bytes)? .Outputs[input.OutputIndex]; var verified = prevOutTx != null && SignManager.Verify(hash, input.Signature, input.PublicKey, prevOutTx.PublicKeyHash); //utxo check ブロックの長さに比例してコストが上がってしまう問題アリ var utxoUsed = transactions.SelectMany(x => x.Inputs).Any(ipt => ipt.TransactionId.Bytes != input.TransactionId.Bytes); var redeemable = prevOutTx.PublicKeyHash.IsEqual(HashUtil.RIPEMD_SHA256(input.PublicKey)); inSum = checked (inSum + prevOutTx.Amount); if (!verified || utxoUsed || !redeemable) { throw new ArgumentException(); } } ulong outSum = 0; foreach (var output in tx.Outputs) { if (output.PublicKeyHash is null || output.Amount <= 0) { throw new ArgumentException(); } outSum = checked (outSum + output.Amount); } if (outSum > inSum) { throw new ArgumentException(); } tx.TransactionFee = inSum - outSum; }
public Transaction ToSignedTransaction(byte[] privateKey, byte[] publicKey) { _transaction.TimeStamp = DateTime.UtcNow; _transaction.Id = null; foreach (var inEntry in Inputs) { inEntry.PublicKey = null; inEntry.Signature = null; } var hash = HashUtil.ComputeTransactionSignHash(JsonSerializer.Serialize(_transaction)); var signature = SignManager.Signature(hash, privateKey, publicKey); foreach (var inEntry in Inputs) { inEntry.PublicKey = publicKey; inEntry.Signature = signature; } var txData = JsonSerializer.Serialize(_transaction); var txHash = HashUtil.DoubleSHA256(txData); _transaction.Id = new HexString(txHash); return(_transaction); }