Logical clocks enable nodes to establish relative order, it does not prevent nodes from lying about when they saw a transaction. Each logical clock is locally owned and the node could falsify the timing or the order of events.
To solve this, actions are associated with the node responsible for creating it to prove the node is faulty in the event of a failure (which provides aBFD). Radix uses what are known as Commitments to audit the logical clock output of nodes, ensure accountability and prevent tampering. A Commitment is a unique identifier created by five bits of information:
The node’s logical clock value at time of Commitment
The ID of the node submitting the Commitment
The timestamp for that node at time of Commitment
A signature from the node to prove it produced the Commitment
A Merkle hash
The initial four are fairly self-evident but the Merkle hash may require explanation. Each protocol event will have a unique reference known as a hash (a long string of text and numbers). We essentially add a number of these hashes together to give us a unique Merkle hash.
We do this as it allows for nodes to check the work of the submitting node. Not all nodes will have seen the relevant events included but still need to know if the Commitment is valid. To make sure the node isn’t lying, other nodes need a means to examine the data. The Merkle Hash is what allows them to accomplish this.
For example, assume a node is reviewing a Commitment from another node which has four events in it, but the reviewing node only knows about Event A. Before validating the Commitment, it first asks neighboring nodes for information on Events B, C and D. This will allow the node to know the order events were seen in (by their logical clock values).
The node can then check the Merkle Hash to make sure that the first node was telling the truth about when Event A occurred and that the four events occurred in that order. If these conditions aren’t met then the Merkle Hash will be different and as such so will the Commitment. This means the submitting node may have lied or tampered with the logical clocks of when it had seen events.
Furthermore, each new Commitment incorporates the information from the previous Commitment into it, which makes it harder to lie about past transactions. Ultimately, this simple technique means that all nodes MUST strictly increment their logical clocks by 1 for each new event as anything else is trivially detectable by the other nodes.
To dive into this a bit more, here is Dan again!
The final problem we face is that nodes could pick and choose when to submit Commitments or not produce Commitments at all. This would make it harder to know when they actually saw events. This is solved by forced periodic Commitments where nodes must share information. This means a node is unable to say it saw Event Z at a particular moment in time and then later lie, ensuring logical clocks increment strictly in order.
As mentioned above, this is a very passive consensus mechanism. It only needs to operate in the case of a conflict. In a network not under attack, this is less than 1 in 10,000 transactions (non-malicious faults can still cause issues). In 95% of cases, the above simple consensus mechanism resolves the conflict in a asynchronous byzantine fault detecting way.
The remaining 5% of non-malicious faults (1 in 200,000) or in the case of some forms of malicious attacks require a more exotic conflict resolution using Mass. This will be the subject of a later blog post. To learn more about the simple Radix consensus mechanism, please see our white paper here: https://papers.radixdlt.com/tempo/latest/