Skip to content

feat(link): custom light client - POC - #1391

Draft
dhfang wants to merge 17 commits into
mainfrom
feat/custom-light-client-cli
Draft

feat(link): custom light client - POC#1391
dhfang wants to merge 17 commits into
mainfrom
feat/custom-light-client-cli

Conversation

@dhfang

@dhfang dhfang commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for custom light-client provers in downstream-compiled ibc binaries.

  • Exposes an importable CLI root command with custom prover factory registration.
  • Adds public prover, factory, registry, and configuration APIs.
  • Includes a remote HTTP prover POC backed by attestation.
  • Validates a complete packet relay through the custom-compiled CLI and remote prover.

I chose to expose the CLI root command and have it accept the custom light client provers as this felt like the option that was the smallest extension of the public surface and the most straightforward for consumers to understand. This also enables consumers of custom light client prover functionality to continue operating Link through the CLI. Alternatives like exposing the relayer service drag more of the internals out into the public interface requiring the consumer to understand a larger set of components and the relayer service lifecycle. Exposing it also feels premature as the relayer service architecture is still not stable and we have not thoroughly thought through or tried to understand the possible use cases that may influence the design of these libraries.

Once the approach is validated, I'll remove the POC specific code and do some additional cleanup before putting up the PR for review. I haven't fully refactored the internal proof generation library to keep the diff small as I mainly wanted feedback on the public API which is as follows:

Public API

Downstream binaries register custom prover factories when constructing the CLI:

func main() {
    registry := lightclient.NewRegistry()

    if err := registry.Register(myclient.Factory{}); err != nil {
        panic(err)
    }

    root := cli.NewRootCmd(cli.Options{
        Relayer: cli.RelayerOptions{
            ProverFactories: registry,
        },
    })

    os.Exit(cli.Execute(root))
}

A factory identifies its client type and constructs one prover for each configured client:

type Factory struct{}

func (Factory) Type() string {
    return "my-client"
}

func (Factory) New(
    ctx context.Context,
    opts lightclient.ProverFactoryOptions,
) (lightclient.Prover, error) {
    var params Params
    if err := opts.Client.ClientParams.Decode(&params); err != nil {
        return nil, err
    }

    return NewProver(
        opts.Client.ClientID,
        opts.HostChain,
        opts.CounterpartyChain,
        params,
    )
}

Custom provers implement the proof-generation contract used by the relayer. The relayer creates one prover per light client instance that is responsible for creating proofs for the light client and indicating what height of the counterparty chain it considers provable

type Prover interface {
    LatestProvableHeight(context.Context) (uint64, time.Time, error)

    StateProof(
        context.Context,
        uint64,
    ) ([]byte, error)

    PacketProofs(
        context.Context,
        uint64,
        ProofKind,
        []channeltypesv2.Packet,
    ) ([][]byte, error)
}

Operators select the custom implementation and provide implementation-specific parameters through the normal Link configuration via a new arbitrarily shaped clientParams:

relayer:
  connections:
    - alias: example
      clientA:
        chainId: "1"
        clientId: "client-a"
        type: my-client
        clientParams:
          endpoint: https://prover.example.com

I've left some comments in the diff to highlight the most important parts.

"github.com/cosmos/ibc/link/lightclient/remotepoc"
)

func main() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an example of how a consumer would construct the binary to compile their custom proving logic into the CLI.

)

// TestRemoteAttestationLightClientRelaysPacket relays through a remote prover.
func TestRemoteAttestationLightClientRelaysPacket(t *testing.T) {

@dhfang dhfang Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E2E POC

Comment thread link/cli/root.go
@@ -0,0 +1,222 @@
// SPDX-License-Identifier: Apache-2.0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposes root command in public api

@@ -64,6 +66,9 @@ type ClientEnd struct {
ClientID string `yaml:"clientId"`
Type ClientType `yaml:"type"`

// ClientParams is interpreted by the factory registered for Type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This accepts arbitrary yaml configuration and lets the custom prover implementations decide how to validate and interpret it.

@@ -0,0 +1,89 @@
// SPDX-License-Identifier: Apache-2.0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation of a server that implements the interface the POC remote prover expects. Executes attestation proof logic.

@@ -0,0 +1,124 @@
// SPDX-License-Identifier: Apache-2.0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POC of how consumers might implement a prover that calls out to a remote service to generate proofs.

)

// Prover generates proofs for one light client.
type Prover interface {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prover interface that consumers would create custom implementations of.

}

// ProverFactory builds provers for one custom light-client type.
type ProverFactory interface {

@dhfang dhfang Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consumers create a factory that the relayer invokes to create provers during startup.

We could extend this to also offer a ValidateParams method that hooks into config validation and lets consumers validate clientParams. This doesn't really feel necessary as I doubt consumers will intend to distribute their binary very broadly and probably don't care as much about the UX as much as we do. They can currently just do some validation in the constructor if they want.

dhfang added 16 commits August 18, 2026 21:13
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
@dhfang
dhfang force-pushed the feat/custom-light-client-cli branch from 87d350b to 80ac2ea Compare August 19, 2026 04:13
Signed-off-by: Dennis Fang <dhsfang@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant