When a transaction is processed initially, it is processed against each node's open ledger. This has three purposes. First, it allows the node to avoid forwarding transactions the node does not believe will succeed. Second, it allows the node to update its open ledger to correctly check subsequent transactions in the same ledger window. Third, it builds the transaction ID tree that the node will use to propose a transaction set during ledger close.
Once a transaction gets into the consensus set, it is then processed again. The reprocessing is needed to ensure that all nodes apply transactions in the same order and thus obtain the same results. During this second processing, it is important to preserve for clients information about what the transaction did.
For example, a ripple transaction may specify multiple paths and may leave open the exact amount of funds needed to enter the transaction. Clients will need to know which path was taken and how much the transaction actually cost them. In addition, clients that wish to traverse their transaction history will need a link to their previous transaction.
This information is called "transaction metadata". It is only accumulated when a transaction is processed as part of a consensus set.
One of the key design principles of the Ripple system is that entries in the ledger (such as ripple balances, quality levels, and so on) can only be manipulated by transactions. The metadata serves to provide direct assurance that a change in a ledger entry was properly authorized and will help to understand and debug any unexpected changes in ledger entries.
Transaction metadata will be stored in the transaction tree in each closed, finalized ledger. Each transaction in the consensus set that was able to apply to the ledger will have a node in that tree. It will be indexed by the transaction ID. All it will contain is the transaction ID and the transaction metadata. (The transaction can be fetched by ID if needed and we can provide query functions for the client that fetch the transaction itself automatically.)
Since all nodes must publish the same ledger, it is imperative that the transaction metadata format be fully-specified and deterministic.
Metadata entries will sorted into a canonical order before being serialized to ensure that all servers build precisely the same metadata for a transaction. We will need to precisely define for each transaction what metadata entries it creates.
Since metadata is only stored in the transaction tree, the metadata for a particular transaction is only present in the ledger in which that transaction was applied. The metadata can always be reconstructed from the previous ledger and the transaction set. You do not need the transaction tree from a ledger to process transactions against that ledger.
Threading is used to allow the owner of an object (or others) to determine all the transactions that affected that object. The threading forms a single-linked list.
An account's set of threads is rooted in the account root node. In the account root node is the head of the master thread for that account. Links in that thread include transactions that affect that account, transactions that create new objects associated with that account, and transactions that destroy an object associated with that account. Linking in transactions that destroy objects is necessary otherwise the account owner could not find the list of transactions affecting that object -- the list head would be lost when the object was deleted. The client can use these threads to find the set of transactions that affected an account, find objects associated with that account, and find the transactions that affected those objects.
In the state tree, every object that can serve as a list head has a field for the transaction ID and the field for the ledger sequence of the head of that tree. Account root nodes and offers are examples of such objects. When a transaction is added to the tree, the transaction ID and ledger ID are placed into the state tree. The old values of those fields are placed in the transaction metadata.
The scheme is designed so that only in rare cases does it require an extra modification of an object. Generally, a transaction that affects an object is added to the thread by modifying the head in the object that was affected anyway.
Issue: Need to discuss whether some of this is redundant with the add-only directory mechanism. For example, if account A creates a ripple credit limit for account B, we don't need to thread to notify account B because the directory logic already does that. The directory logic is more for determining current state and the threading is more for determining how we got here, so they serve different purposes. But we may have some redundancy.
The metadata object contains three elements. The "TransactionIndex" indicates where this transaction fits in the order of transactions executed to produce this ledger. The "TransactionResult" gives the transaction result code, indicating whether the transaction fully succeeded or partially failed. Last, the "AffectedNodes" entry lists every ledger node that was affected by the transaction.
The "AffectedNodes" entry contains "ModifiedNode", "DeletedNode", and "CreatedNode" entries corresponding to ledger entries that were modified, deleted, and created.
For modified nodes, both the previous and new values for key fields are provided. For deleted nodes, the value prior to deletion is provided for key fields. Note that if an offer is partially consumed and then deleted because it was unfunded, the "FinalFields" object will contain the final values prior to the deletion.
If a successful payment transaction delivers an amount other than the maximum (as specified in the "Amount" field), the metadata will include a "DeliveredAmount" field containing the amount actually delivered to the destination. This is only possible if the partial payment flag is set.