BTC#: Verifying Transactions

Series: BTC# – Learning to Program Bitcoin in C#

« Previous: Spending with Combined Scripts

There are several things that a node does to verify a transaction. Programming Bitcoin covers two of them in detail: making sure that coins aren’t being created out of nothing and checking that signature scripts correctly unlock the value they’re spending.

TransactionValidation.png

Fee Calculation

To ensure that coins aren’t being created out of nothing we check the value of the inputs compared to the value of the outputs on a transaction. The value of the inputs must be equal to or greater than the value of the outputs. Where they’re not equal, the difference is a fee collected by the miners as an incentive for their work.

Transaction inputs don’t contain an amount. We have to use their references to previous transactions to know what their values are. We need some way of looking up the previous transaction data to do that. If you don’t have it locally, you need some other provider.

I couldn’t get the URLs in the book to work to return any data and so I used an API from blockchain.info to get the data.

I created a transaction repo interface so that other implementations can be easily switched in and then built a concrete class that talks to blockchain.info.

public class TxRepoBlockchainDotInfo : ITxRepo
{
    private static Dictionary<string, Transaction> TxCache = new Dictionary<string, Transaction>();

    public async Task<Transaction> FetchAsync(string txId, Network network = Network.MainNet, bool force = false)
    {
        if (force || !TxCache.ContainsKey(txId))
        {
            var subdomain = (network == Network.TestNet) ? "testnet." : "";
            var url = $"https://{subdomain}blockchain.info/rawtx/{txId}?format=hex";
            var client = new HttpClient();
            var txBytes = (await client.GetStringAsync(url)).GetBytesFromHex();
            var tx = Transaction.Parse(new BinaryReader(new MemoryStream(txBytes)), this);

            var expectedTxId = txId.GetBytesFromHex().Reverse().ToArray().EncodeAsHex();
            if (tx.Id != expectedTxId)
            {
                throw new ValidationException($"Transaction id doesn't match. Expecting {txId}; was {tx.Id}.");
            }

            TxCache[txId] = tx;
        }
        return TxCache[txId];
    }
}

There a dictionary to cache transactions in and a Network enum to let us choose between testnet and mainnet transactions. The method is asynchronous to allow us to await a potentially long-running  HTTP call. Once the data has been returned, we parse the data using the transaction parsing code that we covered previously.

If you don’t want to make the calling code asynchronous you can get a task awaiter and wait for the call to complete before carrying on. I’ve done this in the test code.

var tx = repo.FetchAsync(txId).GetAwaiter().GetResult();

With the transaction lookup code working, we can modify the TxInput class to get information about the output that it’s spending. The request for that output is expensive, since we’re going to the network to find it, so I’ve used lazy evaluation to defer getting it until I really need it.

Output = new Lazy<TxOutput>(() =>
    {
        var tx = txRepo.FetchAsync(PreviousTxId.ToByteArray(ByteArrayFormat.BigEndianUnsigned, 32).EncodeAsHex()).GetAwaiter().GetResult();
        return tx.Outputs[PreviousTxIndex];
    });

This uses C#’s Lazy class to defer the creation of the previous Transaction object until it’s needed. The initialiser function doesn’t run until the value of the Output is requested.

public ulong Amount
{
    get { return Output.Value.Amount; }
}

We use the input Amount property to calculate the transaction fee. If we never request the fee, the look up of the previous transaction never occurs.

On the Transaction class we can now create the Fee property.

public long Fee
{
    get
    {
        return Inputs.Sum(i => (long)i.Amount) - Outputs.Sum(o => (long)o.Amount);
    }
}

This will be used later in validating the Transaction.

Signature Verification

There’s a bit of work to be done to verify the signatures. To create and then verify the signature we need a document hash. (See Digital Signatures as a reminder.) We can’t just hash the transaction document to get the hash, because the document contains the signatures and that would put us in the circular situation where we need the signatures to create the hash, but we need the hash to create the signatures.

The book goes through the steps to create what’s called the “sighash” in detail. In essence, we need to remove the script sigs from the transation inputs and replace them with the pubkey scripts from the outputs they refer to.

We can expose those scripts the same way we did the amounts.

public Script ScriptPubKey
{
    get { return Output.Value.ScriptPubKey; }
}

To get the sighash, we need to modify the way that the transaction inputs are serialised. To support multiple serialisation methods, I’ve added a SigHashType enumeration.

public enum SigHashType
{
    All = 1,
    None = 2,
    Single = 3,
    AnyoneCanPay = 0x80
}

The only value we’ll worry about here is SIGHASH_ALL.

I’ve modified the Serialise() method to take a SigHashType and use that to decide how to serialise the input.

public void Serialise(BinaryWriter writer, SigHashType? sigHashType = null)
{
    writer.Write(PreviousTxId.ToByteArray(ByteArrayFormat.LittleEndianUnsigned, 32));
    writer.Write(PreviousTxIndex);
    if (sigHashType != null)
    {
        ScriptPubKey.Serialise(writer);
    }
    else
    {
        ScriptSig.Serialise(writer);
    }
    writer.Write(Sequence);
}

The Transaction.Serialise() method has also been changed, to add the SigHashType value onto the end of the transaction data.

public void Serialise(BinaryWriter writer, SigHashType? sigHashType = null)
{
    ...
    if (sigHashType != null)
    {
        writer.Write((int)sigHashType);
    }
}

The SigHash of the entire transaction can now be exposed through a new property.

public BigInteger SigHash
{
    get
    {
        var stream = new MemoryStream();
        Serialise(new BinaryWriter(stream), SigHashType.All);
        return stream.ToArray().DoubleSha256().ToBigInteger(ByteArrayFormat.BigEndianUnsigned);
    }
}

Transaction Verification

All of the pieces can now be wired up to verify the entire transaction. This involves checking the amounts of the inputs and outputs and running the combined locking and unlocking scripts to make sure that all the input signatures are valid.

I’ve created a TransactionHandler class to hold this responsibility. Pulling this out into a separate class keeps the transaction code decoupled from the stack machine code. Neither needs a reference to the other.

public class TransactionHandler
{
    public bool VerifyTransaction(Transaction tx)
    {
        var stackMachine = new StackMachine();

        var z = tx.SigHash;

        if (tx.Fee < 0) return false;

        for (int i = 0; i < tx.Inputs.Length; i++)
        {
            if (!VerifyTxInput(tx.Inputs[i], z))
            {
                return false;
            }
        }
        return true;
    }

    public bool VerifyTxInput(TxInput input, BigInteger z)
    {
        var stackMachine = new StackMachine();
        var combinedScript = input.ScriptSig + input.ScriptPubKey;
        return stackMachine.Execute(combinedScript, z);
    }
}

As mentioned in Programming Bitcoin there are other rules that a full node would check but the main points are covered here.

« Previous: Spending with Combined Scripts

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s