Skip to content

Instantly share code, notes, and snippets.

@0xekez
Last active June 4, 2023 05:39
Show Gist options
  • Save 0xekez/2d52cddc53d9b2cd77f42b101f6a5d25 to your computer and use it in GitHub Desktop.
Save 0xekez/2d52cddc53d9b2cd77f42b101f6a5d25 to your computer and use it in GitHub Desktop.

Tendermint consensus requires $\gt\frac{2}{3}$ of validators to sign a block for it to be included in the blockchain. Tendermint light clients are initialized with all the validators on their blockchain. To update a light client, a relayer submits a block. If enough known validators have signed the submitted block, the light client updates its validator set and merkle root to be the same as the submitted block's.

Because Tendermint only includes blocks if $\gt\frac{2}{3}$ of validators agree, $\frac{1}{3}$ of validators being honest will stop a malicious block from being included. Thus, Tendermint light clients can accept new blocks that $\geq \frac{1}{3}$ of validators they know have signed.

Accepting blocks that $\geq \frac{1}{3}$ of validators have signed creates the lightest possible light client, but comes with the tradeoff that the light client is unsafe if more than a third of a blockchain's validators are malicious. One can increase the trust threshold to $\geq\frac{2}{3}$ if they'd like the same safety level as Tendermint consensus. As of January 2023, the Hermes relayer uses a $\frac{2}{3}$ trust threshold by default1.

Thank you to Susannah Evans for reviewing and correcting my many errors as I learned about light clients.

Footnotes

  1. See Josef Widder's comments on trust thresholds for an overview of the tradeoffs of higher thresholds, and cmwalter's analysis of validator set change rates for more on the topic.

@0xekez
Copy link
Author

0xekez commented Jun 4, 2023

I understand this in less detail, but because Bao asked, here's a sketch of how IBC is built on top of a light client like this:

  1. Whenever I want to send a packet, I commit hash(connection|channel|packet_sequence|packet_data) to the sending chain's merkle KV store.
  2. I then relay an update client message to the light client on the chain I'm sending the packet to. This updates the client's merkle root.
  3. I submit a proof to the other chain that I have a connection|channel|packet_sequence|packet_data that hashes to a value which is in the light client's merkle root.
  4. The packet is routed to whatever module it should be sent to.

You can imagine ACKs working the same way, and timeouts being a matter of submiting an update client on the sending chain that contains a block header that is after the specified timeout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment