Ejemplo n.º 1
0
        public void Run(Transaction tx,
                        DateTime blockTime, ulong coinbase = 0,
                        List <TransactionOutput> spentTxo  = null)
        {
            logger.LogDebug($@"Attempt to run TX:{
                tx.Id.ToString().Substring(0, 7)}");

            // Transaction header validity check.
            if (tx.Timestamp > blockTime ||
                !(coinbase == 0 ^ tx.InEntries.Count == 0))
            {
                throw new ArgumentException();
            }

            // In-Entry validity check.
            ulong inSum    = coinbase;
            var   redeemed = new List <TransactionOutput>();
            var   signHash = BlockchainUtil.GetTransactionSignHash(tx.Original);

            foreach (var inEntry in tx.InEntries)
            {
                // Signature check.
                var verified = EccService.Verify(
                    signHash, inEntry.Signature, inEntry.PublicKey);

                // UTXO check. The transaction output must not be spent by
                // previous transactions.
                var txo = new TransactionOutput
                {
                    TransactionId = inEntry.TransactionId,
                    OutIndex      = inEntry.OutEntryIndex,
                };
                var unspent =
                    !(spentTxo?.Contains(txo) ?? false) &&
                    Utxos.TryGetValue(txo, out txo);

                // Recipient address check.
                var addr       = BlockchainUtil.ToAddress(inEntry.PublicKey);
                var redeemable = txo.Recipient.Equals(
                    ByteString.CopyFrom(addr));

                // Sum all the reedemable.
                inSum = checked (inSum + txo.Amount);

                if (!verified || !unspent || !redeemable)
                {
                    throw new ArgumentException();
                }
                redeemed.Add(txo);
            }

            // Out-entry validity check.
            ulong  outSum    = 0;
            ushort outIndex  = 0;
            var    generated = new List <TransactionOutput>();

            foreach (var outEntry in tx.OutEntries)
            {
                if (outEntry.RecipientHash.IsNull() || outEntry.Amount <= 0)
                {
                    throw new ArgumentException();
                }

                // Sum all the transferred.
                outSum = checked (outSum + outEntry.Amount);

                // Create new UTXO entry.
                generated.Add(new TransactionOutput
                {
                    TransactionId = tx.Id,
                    OutIndex      = outIndex++,
                    Recipient     = outEntry.RecipientHash,
                    Amount        = outEntry.Amount,
                });
            }

            // Output exceeds input or coinbase.
            if (outSum > inSum)
            {
                throw new ArgumentException();
            }

            tx.ExecInfo = new TransactionExecInformation
            {
                Coinbase         = coinbase != 0,
                RedeemedOutputs  = redeemed,
                GeneratedOutputs = generated,
                TransactionFee   = inSum - outSum,
            };
        }