Ejemplo n.º 1
0
        // Used by tests.
        internal void ProcessTransactionData(HdAddress address, ICollection <TransactionData> transactions)
        {
            if (transactions.Count == 0)
            {
                return;
            }

            // Convert relevant information in the block to information that can be joined to the wallet tables.
            IWalletTransactionLookup transactionsOfInterest = this.transactionsOfInterest;
            IWalletAddressLookup     addressesOfInterest    = this.addressesOfInterest;

            foreach (TransactionData transactionData in transactions)
            {
                var spendingDetails = transactionData.SpendingDetails;
                if (spendingDetails != null)
                {
                    // TODO: Add block hash to spending details.
                    var block = (spendingDetails.BlockHeight == null) ? null :
                                new HashHeightPair(spendingDetails.BlockHash ?? 0, (int)spendingDetails.BlockHeight);

                    Money totalOut = spendingDetails.Change.Concat(spendingDetails.Payments).Sum(d => d.Amount);

                    this.RecordSpend(block, new TxIn()
                    {
                        PrevOut = new OutPoint(transactionData.Id, transactionData.Index)
                    }, address.ScriptPubKey.ToHex(),
                                     transactionData.SpendingDetails.IsCoinStake ?? false, transactionData.SpendingDetails.CreationTime.ToUnixTimeSeconds(),
                                     totalOut, spendingDetails.TransactionId ?? 0, 0);
                }

                // One per scriptPubKey from scriptPubKey. Original to RedeemScript via TxOut.
                {
                    var block = (transactionData.BlockHeight == null) ? null :
                                new HashHeightPair(transactionData.BlockHash ?? 0, (int)transactionData.BlockHeight);

                    Script scriptPubKey = transactionData.ScriptPubKey;
                    foreach (Script pubKeyScript in this.GetDestinations(scriptPubKey))
                    {
                        bool containsAddress = addressesOfInterest.Contains(pubKeyScript, out AddressIdentifier targetAddress);

                        this.RecordReceipt(block, pubKeyScript, new TxOut(transactionData.Amount ?? 0, scriptPubKey),
                                           (transactionData.IsCoinBase ?? false) || (transactionData.IsCoinStake ?? false),
                                           transactionData.CreationTime.ToUnixTimeSeconds(), transactionData.Id, transactionData.Index, address.AddressType == 1);

                        if (containsAddress)
                        {
                            transactionsOfInterest.AddTentative(new OutPoint(transactionData.Id, transactionData.Index), targetAddress);
                        }
                    }
                }
            }
        }
Ejemplo n.º 2
0
        public bool ProcessTransactions(IEnumerable <Transaction> transactions, HashHeightPair block, uint256 fixedTxId = null)
        {
            bool additions = false;

            // Convert relevant information in the block to information that can be joined to the wallet tables.
            IWalletTransactionLookup transactionsOfInterest = this.transactionsOfInterest;
            IWalletAddressLookup     addressesOfInterest    = this.addressesOfInterest;

            // Used for tracking address top-up requirements.
            var trackers = new Dictionary <TopUpTracker, TopUpTracker>();

            foreach (Transaction tx in transactions)
            {
                // Build temp.PrevOuts
                uint256 txId       = fixedTxId ?? tx.GetHash();
                bool    addSpendTx = false;

                for (int i = 0; i < tx.Inputs.Count; i++)
                {
                    TxIn txIn = tx.Inputs[i];

                    if (transactionsOfInterest.Contains(txIn.PrevOut, out HashSet <AddressIdentifier> addresses))
                    {
                        // Record our outputs that are being spent.
                        foreach (AddressIdentifier address in addresses)
                        {
                            RecordSpend(block, txIn, address.ScriptPubKey, tx.IsCoinBase | tx.IsCoinStake, tx.Time, tx.TotalOut, txId, i);
                        }

                        additions  = true;
                        addSpendTx = true;
                    }
                }

                // Build temp.Outputs.
                for (int i = 0; i < tx.Outputs.Count; i++)
                {
                    TxOut txOut = tx.Outputs[i];

                    if (txOut.IsEmpty)
                    {
                        continue;
                    }

                    if (txOut.ScriptPubKey.ToBytes(true)[0] == (byte)OpcodeType.OP_RETURN)
                    {
                        continue;
                    }

                    foreach (Script pubKeyScript in this.GetDestinations(txOut.ScriptPubKey))
                    {
                        bool containsAddress = addressesOfInterest.Contains(pubKeyScript, out AddressIdentifier address);

                        // Paying to one of our addresses?
                        if (addSpendTx || containsAddress)
                        {
                            // Check if top-up is required.
                            if (containsAddress && address != null)
                            {
                                // Get the top-up tracker that applies to this account and address type.
                                ITopUpTracker tracker = this.GetTopUpTracker(address);
                                if (!tracker.IsWatchOnlyAccount)
                                {
                                    // If an address inside the address buffer is being used then top-up the buffer.
                                    while (address.AddressIndex >= tracker.NextAddressIndex)
                                    {
                                        AddressIdentifier newAddress = tracker.CreateAddress();

                                        // Add the new address to our addresses of interest.
                                        addressesOfInterest.AddTentative(Script.FromHex(newAddress.ScriptPubKey),
                                                                         new AddressIdentifier()
                                        {
                                            WalletId     = newAddress.WalletId,
                                            AccountIndex = newAddress.AccountIndex,
                                            AddressType  = newAddress.AddressType,
                                            AddressIndex = newAddress.AddressIndex
                                        });
                                    }
                                }
                            }

                            // Record outputs received by our wallets.
                            this.RecordReceipt(block, pubKeyScript, txOut, tx.IsCoinBase | tx.IsCoinStake, tx.Time, txId, i, containsAddress && address.AddressType == 1);

                            additions = true;

                            if (containsAddress)
                            {
                                transactionsOfInterest.AddTentative(new OutPoint(txId, i), address);
                            }
                        }
                    }
                }
            }

            return(additions);
        }
Ejemplo n.º 3
0
        public bool ProcessTransactions(IEnumerable <Transaction> transactions, HashHeightPair block, uint256 fixedTxId = null, long?blockTime = null)
        {
            bool additions = false;

            // Convert relevant information in the block to information that can be joined to the wallet tables.
            IWalletTransactionLookup transactionsOfInterest = this.transactionsOfInterest;
            IWalletAddressLookup     addressesOfInterest    = this.addressesOfInterest;

            long currentTime = this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp();

            foreach (Transaction tx in transactions)
            {
                // Build temp.PrevOuts
                uint256 txId       = fixedTxId ?? tx.GetHash();
                bool    addSpendTx = false;

                for (int i = 0; i < tx.Inputs.Count; i++)
                {
                    TxIn txIn = tx.Inputs[i];

                    if (transactionsOfInterest.Contains(txIn.PrevOut, out HashSet <AddressIdentifier> addresses))
                    {
                        // Record our outputs that are being spent.
                        foreach (AddressIdentifier address in addresses)
                        {
                            RecordSpend(block, txIn, address.ScriptPubKey, tx.IsCoinBase | tx.IsCoinStake, blockTime ?? currentTime, tx.TotalOut, txId, i);
                        }

                        additions  = true;
                        addSpendTx = true;
                    }
                }

                // Build temp.Outputs.
                for (int i = 0; i < tx.Outputs.Count; i++)
                {
                    TxOut txOut = tx.Outputs[i];

                    if (txOut.IsEmpty)
                    {
                        continue;
                    }

                    byte[] scriptPubKeyBytes = txOut.ScriptPubKey.ToBytes(true);

                    if (scriptPubKeyBytes.Length == 0 || scriptPubKeyBytes[0] == (byte)OpcodeType.OP_RETURN)
                    {
                        continue;
                    }

                    var destinations = this.GetDestinations(txOut.ScriptPubKey);

                    bool isChange = destinations.Any(d => addressesOfInterest.Contains(d, out AddressIdentifier address2) && address2.AddressType == 1);

                    if (addSpendTx)
                    {
                        // TODO: Why is this done? If the receipt is not to one of our addresses (i.e. identified in the loop coming next) then why bother trying to record it?
                        this.RecordReceipt(block, null, txOut, tx.IsCoinBase | tx.IsCoinStake, blockTime ?? currentTime, txId, i, isChange);
                    }

                    foreach (Script pubKeyScript in destinations)
                    {
                        bool containsAddress = addressesOfInterest.Contains(pubKeyScript, out AddressIdentifier address);

                        // Paying to one of our addresses?
                        if (containsAddress)
                        {
                            // Check if top-up is required.
                            if (containsAddress && address != null)
                            {
                                // Get the top-up tracker that applies to this account and address type.
                                ITopUpTracker tracker = this.GetTopUpTracker(address);
                                if (!tracker.IsWatchOnlyAccount)
                                {
                                    // If an address inside the address buffer is being used then top-up the buffer.
                                    while (address.AddressIndex >= tracker.NextAddressIndex)
                                    {
                                        AddressIdentifier newAddress = tracker.CreateAddress();

                                        // Add the new address to our addresses of interest.
                                        addressesOfInterest.AddTentative(Script.FromHex(newAddress.ScriptPubKey), newAddress);
                                    }
                                }
                            }

                            // TODO: This is a bit of a conundrum - by recording a coldstaking output as having a value, we cause all the effects that the filtration at the higher
                            // layers is intended to prevent. So it would be a lot simpler if we could just set the output value to 0 here if it is a coldstaking script. But that
                            // will most likely have unintended consequences on areas of the code that do expect the outputs to have a value, such as staking.

                            // Record outputs received by our wallets.
                            this.RecordReceipt(block, pubKeyScript, txOut, tx.IsCoinBase | tx.IsCoinStake, blockTime ?? currentTime, txId, i, containsAddress && address.AddressType == 1);

                            additions = true;

                            if (containsAddress)
                            {
                                transactionsOfInterest.AddTentative(new OutPoint(txId, i), address);
                            }
                        }
                    }
                }
            }

            return(additions);
        }
Ejemplo n.º 4
0
        public bool ProcessTransactions(IEnumerable <Transaction> transactions, HashHeightPair block, uint256 fixedTxId = null, long?blockTime = null)
        {
            bool additions = false;

            // Convert relevant information in the block to information that can be joined to the wallet tables.
            IWalletTransactionLookup transactionsOfInterest = this.transactionsOfInterest;
            IWalletAddressLookup     addressesOfInterest    = this.addressesOfInterest;

            long currentTime = this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp();

            foreach (Transaction tx in transactions)
            {
                // Build temp.PrevOuts
                uint256 txId       = fixedTxId ?? tx.GetHash();
                bool    addSpendTx = false;

                for (int i = 0; i < tx.Inputs.Count; i++)
                {
                    TxIn txIn = tx.Inputs[i];

                    if (transactionsOfInterest.Contains(txIn.PrevOut, out HashSet <AddressIdentifier> addresses))
                    {
                        // Record our outputs that are being spent.
                        foreach (AddressIdentifier address in addresses)
                        {
                            RecordSpend(block, txIn, address.ScriptPubKey, tx.IsCoinBase | tx.IsCoinStake, blockTime ?? currentTime, tx.TotalOut, txId, i);
                        }

                        additions  = true;
                        addSpendTx = true;
                    }
                }

                // Build temp.Outputs.
                for (int i = 0; i < tx.Outputs.Count; i++)
                {
                    TxOut txOut = tx.Outputs[i];

                    if (txOut.IsEmpty)
                    {
                        continue;
                    }

                    byte[] scriptPubKeyBytes = txOut.ScriptPubKey.ToBytes(true);

                    if (scriptPubKeyBytes.Length == 0 || scriptPubKeyBytes[0] == (byte)OpcodeType.OP_RETURN)
                    {
                        continue;
                    }

                    var destinations = this.GetDestinations(txOut.ScriptPubKey);

                    bool isChange = destinations.Any(d => addressesOfInterest.Contains(d.ScriptPubKey, out AddressIdentifier address2) && address2.AddressType == 1);

                    if (addSpendTx)
                    {
                        // TODO: Why is this done? If the receipt is not to one of our addresses (i.e. identified in the loop coming next) then why bother trying to record it?
                        this.RecordReceipt(block, null, txOut, tx.IsCoinBase | tx.IsCoinStake, blockTime ?? currentTime, txId, i, isChange);
                    }

                    foreach (TxDestination destination in destinations)
                    {
                        var  pubKeyScript    = destination.ScriptPubKey;
                        bool containsAddress = addressesOfInterest.Contains(pubKeyScript, out AddressIdentifier address);

                        // Paying to one of our addresses?
                        if (containsAddress && address != null)
                        {
                            // This feature, by design, is agnostic of the type of template being processed.
                            // This type of check is good to have for cold staking though but is catered for in broader terms.
                            // I.e. don't allow any funny business with keys being used with accounts they were not intended for.
                            if (destination is IAccountRestrictedKeyId accountRestrictedKey)
                            {
                                if (accountRestrictedKey.AccountId != address.AccountIndex)
                                {
                                    continue;
                                }
                            }
                            else
                            {
                                // This tests the converse.
                                // Don't allow special-purpose accounts (e.g. coldstaking) to be used like normal accounts.
                                if (address.AccountIndex >= Wallet.SpecialPurposeAccountIndexesStart)
                                {
                                    continue;
                                }
                            }

                            // Check if top-up is required.
                            // Get the top-up tracker that applies to this account and address type.
                            ITopUpTracker tracker = this.GetTopUpTracker(address);
                            if (!tracker.IsWatchOnlyAccount)
                            {
                                // If an address inside the address buffer is being used then top-up the buffer.
                                while (address.AddressIndex >= tracker.NextAddressIndex)
                                {
                                    AddressIdentifier newAddress = tracker.CreateAddress();

                                    // Add the new address to our addresses of interest.
                                    addressesOfInterest.AddTentative(Script.FromHex(newAddress.ScriptPubKey), newAddress);
                                }
                            }

                            // Record outputs received by our wallets.
                            this.RecordReceipt(block, pubKeyScript, txOut, tx.IsCoinBase | tx.IsCoinStake, blockTime ?? currentTime, txId, i, address.AddressType == 1);

                            additions = true;

                            transactionsOfInterest.AddTentative(new OutPoint(txId, i), address);
                        }
                    }
                }
            }

            return(additions);
        }