BitSharper.Transaction.SignInputs C# (CSharp) Method

SignInputs() public method

Once a transaction has some inputs and outputs added, the signatures in the inputs can be calculated. The signature is over the transaction itself, to prove the redeemer actually created that transaction, so we have to do this step last.
This method is similar to SignatureHash in script.cpp
public SignInputs ( SigHash hashType, Wallet wallet ) : void
hashType SigHash This should always be set to SigHash.ALL currently. Other types are unused.
wallet Wallet A wallet is required to fetch the keys needed for signing.
return void
        public void SignInputs(SigHash hashType, Wallet wallet)
        {
            Debug.Assert(_inputs.Count > 0);
            Debug.Assert(_outputs.Count > 0);

            // I don't currently have an easy way to test other modes work, as the official client does not use them.
            Debug.Assert(hashType == SigHash.All);

            // The transaction is signed with the input scripts empty except for the input we are signing. In the case
            // where addInput has been used to set up a new transaction, they are already all empty. The input being signed
            // has to have the connected OUTPUT program in it when the hash is calculated!
            //
            // Note that each input may be claiming an output sent to a different key. So we have to look at the outputs
            // to figure out which key to sign with.

            var signatures = new byte[_inputs.Count][];
            var signingKeys = new EcKey[_inputs.Count];
            for (var i = 0; i < _inputs.Count; i++)
            {
                var input = _inputs[i];
                Debug.Assert(input.ScriptBytes.Length == 0, "Attempting to sign a non-fresh transaction");
                // Set the input to the script of its output.
                input.ScriptBytes = input.Outpoint.ConnectedPubKeyScript;
                // Find the signing key we'll need to use.
                var connectedPubKeyHash = input.Outpoint.ConnectedPubKeyHash;
                var key = wallet.FindKeyFromPubHash(connectedPubKeyHash);
                // This assert should never fire. If it does, it means the wallet is inconsistent.
                Debug.Assert(key != null, "Transaction exists in wallet that we cannot redeem: " + Utils.BytesToHexString(connectedPubKeyHash));
                // Keep the key around for the script creation step below.
                signingKeys[i] = key;
                // The anyoneCanPay feature isn't used at the moment.
                const bool anyoneCanPay = false;
                var hash = HashTransactionForSignature(hashType, anyoneCanPay);
                // Set the script to empty again for the next input.
                input.ScriptBytes = TransactionInput.EmptyArray;

                // Now sign for the output so we can redeem it. We use the keypair to sign the hash,
                // and then put the resulting signature in the script along with the public key (below).
                using (var bos = new MemoryStream())
                {
                    bos.Write(key.Sign(hash));
                    bos.Write((byte) (((int) hashType + 1) | (anyoneCanPay ? 0x80 : 0)));
                    signatures[i] = bos.ToArray();
                }
            }

            // Now we have calculated each signature, go through and create the scripts. Reminder: the script consists of
            // a signature (over a hash of the transaction) and the complete public key needed to sign for the connected
            // output.
            for (var i = 0; i < _inputs.Count; i++)
            {
                var input = _inputs[i];
                Debug.Assert(input.ScriptBytes.Length == 0);
                var key = signingKeys[i];
                input.ScriptBytes = Script.CreateInputScript(signatures[i], key.PubKey);
            }

            // Every input is now complete.
        }

Usage Example

        /// <summary>
        /// Creates a transaction that sends $coins.$cents BTC to the given address.
        /// </summary>
        /// <remarks>
        /// IMPORTANT: This method does NOT update the wallet. If you call createSend again you may get two transactions
        /// that spend the same coins. You have to call confirmSend on the created transaction to prevent this,
        /// but that should only occur once the transaction has been accepted by the network. This implies you cannot have
        /// more than one outstanding sending tx at once.
        /// </remarks>
        /// <param name="address">The BitCoin address to send the money to.</param>
        /// <param name="nanocoins">How much currency to send, in nanocoins.</param>
        /// <param name="changeAddress">
        /// Which address to send the change to, in case we can't make exactly the right value from
        /// our coins. This should be an address we own (is in the keychain).
        /// </param>
        /// <returns>
        /// A new <see cref="Transaction"/> or null if we cannot afford this send.
        /// </returns>
        internal Transaction CreateSend(Address address, ulong nanocoins, Address changeAddress)
        {
            lock (this)
            {
                _log.Info("Creating send tx to " + address + " for " +
                          Utils.BitcoinValueToFriendlyString(nanocoins));
                // To send money to somebody else, we need to do gather up transactions with unspent outputs until we have
                // sufficient value. Many coin selection algorithms are possible, we use a simple but suboptimal one.
                // TODO: Sort coins so we use the smallest first, to combat wallet fragmentation and reduce fees.
                var valueGathered = 0UL;
                var gathered = new LinkedList<TransactionOutput>();
                foreach (var tx in Unspent.Values)
                {
                    foreach (var output in tx.Outputs)
                    {
                        if (!output.IsAvailableForSpending) continue;
                        if (!output.IsMine(this)) continue;
                        gathered.AddLast(output);
                        valueGathered += output.Value;
                    }
                    if (valueGathered >= nanocoins) break;
                }
                // Can we afford this?
                if (valueGathered < nanocoins)
                {
                    _log.Info("Insufficient value in wallet for send, missing " +
                              Utils.BitcoinValueToFriendlyString(nanocoins - valueGathered));
                    // TODO: Should throw an exception here.
                    return null;
                }
                Debug.Assert(gathered.Count > 0);
                var sendTx = new Transaction(_params);
                sendTx.AddOutput(new TransactionOutput(_params, sendTx, nanocoins, address));
                var change = (long) (valueGathered - nanocoins);
                if (change > 0)
                {
                    // The value of the inputs is greater than what we want to send. Just like in real life then,
                    // we need to take back some coins ... this is called "change". Add another output that sends the change
                    // back to us.
                    _log.Info("  with " + Utils.BitcoinValueToFriendlyString((ulong) change) + " coins change");
                    sendTx.AddOutput(new TransactionOutput(_params, sendTx, (ulong) change, changeAddress));
                }
                foreach (var output in gathered)
                {
                    sendTx.AddInput(output);
                }

                // Now sign the inputs, thus proving that we are entitled to redeem the connected outputs.
                sendTx.SignInputs(Transaction.SigHash.All, this);
                _log.InfoFormat("  created {0}", sendTx.HashAsString);
                return sendTx;
            }
        }