The attestation light client replaces header verification with a signature check. Instead of following a counterparty chain’s consensus, it trusts a fixed set of ECDSA attestors and accepts a height, a timestamp or a set of packet commitments once a quorum of that set has signed it.The Go implementation is the package attestations in modules/light-clients/attestations, imported as:
The attestor set and the quorum threshold are fixed at client creation. ClientState.Validate (client_state.go) rejects a client state whose attestor list is empty, whose MinRequiredSigs is zero or exceeds the number of attestors, or whose attestor list contains an empty address, an address that is not a hex address, or a duplicate after normalization.There is no mechanism to add, remove or rotate attestors afterwards. The only state transition available to the client beyond adding consensus states is being frozen.
One field, Timestamp uint64, in nanoseconds. ValidateBasic rejects a zero timestamp. GetTimestamp() is deprecated and panics; read the timestamp through LightClientModule.TimestampAtHeight, which returns the stored nanosecond value (consensus_state.go).Consensus states are stored per height under the standard host.ConsensusStateKey in the client’s prefixed store (store.go).
Both client updates and proofs use the same envelope, AttestationProof, which implements exported.ClientMessage:
Field
Go type
Description
AttestationData
[]byte
ABI-encoded StateAttestation or PacketAttestation
Signatures
[][]byte
Each exactly SignatureLength (65) bytes, r || s || v
ValidateBasic (attestation_proof.go) requires non-empty attestation data, requires the data to decode as either a PacketAttestation with at least one packet or a StateAttestation, requires at least one signature, and requires each signature to be 65 bytes.The two attestation payloads are Go structs in abi.go:
type StateAttestation struct { Height uint64 Timestamp uint64 // nanoseconds in Go; seconds on the wire}type PacketAttestation struct { Height uint64 Packets []PacketCompact}type PacketCompact struct { Path []byte // keccak256 of the commitment path, 32 bytes Commitment []byte // raw 32-byte commitment; 32 zero bytes means absent}
StateAttestation is ABI-encoded as two uint64 arguments, (height, timestamp). PacketAttestation is encoded as a single tuple argument holding a uint64 height and a dynamic array of (bytes32 path, bytes32 commitment) tuples. The encoding is what makes the same attestor output usable by the EVM implementation; see attestation light client contracts for that side. On the Go side it is go-ethereum’s accounts/abi package and nothing more.
StateAttestation.ABIEncode divides the timestamp by 109 and ABIDecodeStateAttestation multiplies it back. The wire value is seconds; the value stored in the consensus state is nanoseconds. Sub-second precision does not survive a round trip.
where typeTag is one byte: AttestationTypeState (0x01) for client updates, AttestationTypePacket (0x02) for membership and non-membership proofs. The tag is what stops a packet attestation being replayed as a state attestation.ClientState.verifySignatures then, for the proof’s signatures:
refuses an empty signature list, and refuses a list shorter than MinRequiredSigs;
refuses any signature that is not 65 bytes;
normalizes the recovery byte — 27 becomes 0, 28 becomes 1, anything else is left alone — and recovers the signer with crypto.SigToPub;
refuses a repeated signer (ErrDuplicateSigner);
refuses a signer outside the attestor set (ErrUnknownSigner).
Quorum is counted as the number of signatures supplied, not the number that verified — every signature in the list must recover to a distinct attestor, so the two coincide, but a proof carrying extra junk signatures is rejected outright rather than ignored.
VerifyClientMessage refuses a frozen client, refuses a client message that is not an *AttestationProof, and verifies the signatures against the 0x01 tag. It does not inspect the height or the timestamp.UpdateState (update.go) then decodes the StateAttestation, writes a ConsensusState at clienttypes.NewHeight(0, stateAttestation.Height), raises LatestHeight if the attested height is greater, and stores the client state. An update may therefore write a consensus state at a height at or below LatestHeight.UpdateState panics rather than returning an error on a wrong client message type or undecodable data, on the assumption that VerifyClientMessage ran first.
CheckForMisbehaviour decodes the state attestation, looks up the consensus state already stored at that height, and returns true when one exists with a different timestamp. UpdateStateOnMisbehaviour sets IsFrozen and writes the client state back; nothing unfreezes it.Note that a consensus state is written at a height only once a timestamp has been attested for it. Resubmitting the same height with the same timestamp is not misbehaviour.
verifyMembership and verifyNonMembership (client_state.go) share their preconditions. Both refuse a frozen client and an empty path, both require a consensus state to already exist at the proof height, both unmarshal the proof bytes as a protobuf AttestationProof, both verify signatures against the 0x02 tag, both require packetAttestation.Height to equal the proof height’s revision height, and both require a non-empty packet list.The path must be a commitmenttypesv2.MerklePath with exactly one key element, and the comparison key is crypto.Keccak256(merklePath.KeyPath[0]).
The value must be exactly 32 bytes. Verification scans the attested packets for an entry whose Path and Commitment are both 32 bytes and equal to the hashed path and the value respectively. No match is ErrNotMember.
Verification scans for entries whose Path matches the hashed path. If none match, the result is ErrNotMember — absence from the attested list does not prove absence on the counterparty. If entries match but any of them has a commitment that is not 32 zero bytes, the result is ErrNonMembershipFailed. The attestors have to assert the empty path explicitly.delayTimePeriod and delayBlockPeriod are accepted by the LightClientModule methods and not used.
NewLightClientModule(cdc, storeProvider) returns a LightClientModule implementing exported.LightClientModule. Register it with the client keeper under ibcattestations.ModuleName (attestations), which becomes the client type prefix:
Initialize unmarshals the client state and the consensus state, runs Validate and ValidateBasic on them, and writes the consensus state at clienttypes.NewHeight(0, clientState.LatestHeight) — so the LatestHeight in the initial client state and the timestamp in the initial consensus state must describe the same height, and that height is the only one a proof can be verified against until an update lands.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"IBC Documentation","description":"Documentation for the Inter-Blockchain Communication protocol, its Go implementation, and the IBC CLI.","colors":{"primary":"#000000","light":"#cccccc","dark":"#0E0E0E"},"logo":{"light":"https://mintcdn.com/ibc-docs/_IzGrVOELf0DsHtq/mintlify/assets/brand/ibc-wordmark-dark.svg?fit=max\u0026auto=format\u0026n=_IzGrVOELf0DsHtq\u0026q=85\u0026s=dd69509f8e278637cefba78620bc6f19","dark":"https://mintcdn.com/ibc-docs/_IzGrVOELf0DsHtq/mintlify/assets/brand/ibc-wordmark.svg?fit=max\u0026auto=format\u0026n=_IzGrVOELf0DsHtq\u0026q=85\u0026s=4332fd7d24e0e9834a0ff6d9b0907db8","href":"/"},"favicon":{"light":"https://mintcdn.com/ibc-docs/_IzGrVOELf0DsHtq/mintlify/assets/brand/ibc-dark.svg?fit=max\u0026auto=format\u0026n=_IzGrVOELf0DsHtq\u0026q=85\u0026s=950310224e31e8dcbbbae521db80542e","dark":"https://mintcdn.com/ibc-docs/_IzGrVOELf0DsHtq/mintlify/assets/brand/ibc.svg?fit=max\u0026auto=format\u0026n=_IzGrVOELf0DsHtq\u0026q=85\u0026s=8846e23ba7502345c1b7016d0ec9315d"},"navbar":{"primary":{"type":"button","label":"Get started","href":"/ibc/05-ibc-cli/02-tutorial/01-deploy-ibc-and-send-a-token"}},"footer":{"socials":{"x":"https://twitter.com/cosmos","github":"https://github.com/cosmos","discord":"https://discord.gg/interchain","website":"https://cosmos.network"},"links":[{"header":"Documentation","items":[{"label":"Learn how IBC works","href":"/ibc/01-introduction/01-what-is-ibc"},{"label":"IBC CLI","href":"/ibc/05-ibc-cli/01-overview"},{"label":"IBC-Solidity","href":"/ibc/06-ibc-solidity-contracts/01-overview"},{"label":"IBC-Go","href":"/ibc-go/latest/intro"},{"label":"Specification","href":"/ibc/spec/ics-001-ics-standard/README"}]},{"header":"Repositories","items":[{"label":"cosmos/ibc","href":"https://github.com/cosmos/ibc"},{"label":"cosmos/ibc-go","href":"https://github.com/cosmos/ibc-go"},{"label":"cosmos/ibc-contracts","href":"https://github.com/cosmos/ibc-contracts"}]},{"header":"Security","items":[{"label":"Security audits","href":"/ibc/07-security/01-audits"},{"label":"Bug Bounty","href":"https://immunefi.com/bug-bounty/cosmos/information"}]}]},"icons":{"library":"lucide"},"styling":{"codeblocks":{"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}},"latex":false},"contextual":{"options":["copy","view","chatgpt","claude","perplexity","mcp","cursor","vscode"],"display":"header"},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"ibc-docs","hasAudioNarration":false,"actualSubdomain":"ibc-docs","customDomains":[],"gitSource":{"type":"github","owner":"cosmos","repo":"ibc","deployBranch":"main","contentDirectory":"docs","isPrivate":false,"mountPath":"","label":"","_id":"6ae38e8fd265945f8832a739"},"chroma":{"latestBuiltDeploymentHistoryId":"6ae38f56d265945f8832b6ee","collectionId":"e8081330-a92e-4d86-a023-d2707473bf51","liveDeploymentHistoryId":"6ae38f56d265945f8832b6ee","schemaVersion":"v3"},"entitlements":{},"buildId":"6ae393af0b369713606c9b5e","clientVersion":"0.0.4021","searchFilterCounts":[{"language":"default","tag":"Getting Started","count":2}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"/docs","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"mintlify/style.css","content":"/* cyrillic-ext */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh0NSDulI.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh2dSDulI.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh0dSDulI.woff2) format('woff2');\n unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh3tSDulI.woff2) format('woff2');\n unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bhZ_Wmh2uX.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh09SDulI.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh3dSD.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh0NSDulI.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh2dSDulI.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* greek-ext */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh0dSDulI.woff2) format('woff2');\n unicode-range: U+1F00-1FFF;\n}\n/* greek */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh3tSDulI.woff2) format('woff2');\n unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bhZ_Wmh2uX.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh09SDulI.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Fira Code';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/docs/_mintlify/assets/fonts/google/s/firacode/v27/uU9NCBsR6Z2vfE9aq3bh3dSD.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n:root{--background-light:255 255 255}.bg-primary\\/10,.dark .bg-primary\\/10,.dark\\:bg-primary-light\\/10:is(.dark *){background-color:var(--nav-accent, #40b3ff)}.bg-primary\\/10 .text-primary,.bg-primary\\/10.text-primary,.dark\\:bg-primary-light\\/10:is(.dark *),.dark\\:bg-primary-light\\/10:is(.dark *) .text-primary,.dark\\:bg-primary-light\\/10:is(.dark *).dark\\:text-primary-light{color:var(--nav-accent-ink, #0e0e0e)}@font-face{font-family:'The Future';src:url(/mintlify/assets/fonts/the-future-regular.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:'The Future';src:url(/mintlify/assets/fonts/the-future-medium.woff2) format('woff2');font-weight:500;font-style:normal;font-display:swap}@font-face{font-family:'The Future';src:url(/mintlify/assets/fonts/the-future-bold.woff2) format('woff2');font-weight:700;font-style:normal;font-display:swap}@font-face{font-family:'The Future';src:url(/mintlify/assets/fonts/the-future-light.woff2) format('woff2');font-weight:300;font-style:normal;font-display:swap}body:has(#custom-index-content) #header,body:has(#custom-index-content) #navbar,body:has(#custom-index-content) #search-bar-entry,body:has(#custom-index-content) #search-bar-entry-mobile,body:has(#custom-index-content) .chat-assistant-floating-input,body:has(#custom-index-content) :has(>.chat-assistant-floating-input),body:has(#custom-index-content) button#search-bar-entry,body:has(#custom-index-content) button#search-bar-entry-mobile,body:has(#custom-index-content) header,body:has(#custom-index-content) nav[aria-label]{display:none!important;visibility:hidden!important;opacity:0!important;width:0!important;height:0!important;position:absolute!important;left:-99999px!important;pointer-events:none!important}body:has(#custom-index-content) #custom-index-content,body:has(#custom-index-content) #custom-index-content a,body:has(#custom-index-content) #custom-index-content h1,body:has(#custom-index-content) #custom-index-content h2,body:has(#custom-index-content) #custom-index-content h3,body:has(#custom-index-content) #custom-index-content p{font-family:'The Future',sans-serif}@media (max-width:768px){button[aria-label=\"More actions\"]{display:none!important}#custom-index-content{margin-top:-120px}}@media (min-width:769px) and (max-width:1023px){#custom-index-content{margin-top:-120px}}.prose .callout a,.prose .info a,.prose .note a,.prose .step a,.prose .steps a,.prose .tip a,.prose .warning a,.prose [role=listitem] a,.prose div[data-callout-type] a,.prose li a,.prose p a,.prose span a,.prose ul a{border-bottom-color:#0e0e0e!important;text-decoration-color:#0e0e0e!important}.dark .prose .callout a,.dark .prose .info a,.dark .prose .note a,.dark .prose .step a,.dark .prose .steps a,.dark .prose .tip a,.dark .prose .warning a,.dark .prose [role=listitem] a,.dark .prose div[data-callout-type] a,.dark .prose li a,.dark .prose p a,.dark .prose span a,.dark .prose ul a{border-bottom-color:#ccc!important;text-decoration-color:#ccc!important}.highlighted-card-bg{background-color:#ffb84d;position:relative;overflow:hidden;transition:box-shadow .3s ease-in-out}.highlighted-card-bg:hover{box-shadow:0 0 30px rgba(255,184,77,.5),0 0 60px rgba(255,184,77,.3),0 10px 40px rgba(255,184,77,.2)}.highlighted-card-bg::before{content:'';position:absolute;top:-50%;left:-50%;width:200%;height:200%;background:linear-gradient(45deg,transparent 30%,rgba(255,255,255,.4) 50%,transparent 70%);transform:translateX(-100%) translateY(-100%) rotate(45deg);transition:transform .6s ease-in-out}.highlighted-card-bg:hover::before{transform:translateX(100%) translateY(100%) rotate(45deg)}@layer utilities{body:has(#custom-index-content) #footer{display:flex!important}}code,pre,pre code{font-family:'Fira Code','Cascadia Code','JetBrains Mono',monospace!important}#navbar a>span.absolute.bg-primary-dark,header a>span.absolute.bg-primary-dark{background-color:var(--nav-accent, #40b3ff);transition:background-color 120ms ease}#navbar a:hover>span.absolute.bg-primary-dark,header a:hover>span.absolute.bg-primary-dark{background-color:color-mix(in srgb,var(--nav-accent, #40b3ff) 82%,white);opacity:1}#navbar a:has(>span.absolute.bg-primary-dark),#navbar a:has(>span.absolute.bg-primary-dark) *,header a:has(>span.absolute.bg-primary-dark),header a:has(>span.absolute.bg-primary-dark) *{color:var(--nav-accent-ink, #0e0e0e);font-weight:600}.nav-tabs-item{--tab-accent:#40b3ff;--tab-ink:#0e0e0e;transition:color 120ms ease}.nav-tabs-item::before{content:\"\";position:absolute;inset:9px -10px;border-radius:.5rem;background-color:transparent;transition:background-color 120ms ease;z-index:-1}.nav-tabs-item:hover::before{background-color:var(--tab-accent)}.nav-tabs-item:hover,.nav-tabs-item:hover *{color:var(--tab-ink)!important}.nav-tabs-item[href^=\"/ibc/05-ibc-cli/\"]{--tab-accent:#22e2a8;--tab-ink:#0e0e0e}.nav-tabs-item[href^=\"/ibc/06-ibc-solidity-contracts/\"]{--tab-accent:#7522b5;--tab-ink:#ffffff}.nav-tabs-item[href^=\"/ibc-go/\"]{--tab-accent:#4b47ca;--tab-ink:#ffffff}.nav-tabs-item[href^=\"/ibc/spec/\"]{--tab-accent:#ef5628;--tab-ink:#0e0e0e}img.nav-logo{height:3rem}"}],"page":{"pagePath":"ibc-go/latest/light-clients/attestation/attestation-light-client","slug":"/ibc-go/latest/light-clients/attestation/attestation-light-client","pageMetadata":{"title":"The attestation light client","description":"How the ibc-go attestation light client verifies state a quorum of attestors has signed.","href":"/ibc-go/latest/light-clients/attestation/attestation-light-client"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"How the ibc-go attestation light client verifies state a quorum of attestors has signed.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Note,Table,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Note)_missingMdxReference(`Note`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`2`,id:`overview`,children:`Overview`}),`\n`,_jsx(_components.p,{children:`The attestation light client replaces header verification with a signature check. Instead of following a counterparty chain’s consensus, it trusts a fixed set of ECDSA attestors and accepts a height, a timestamp or a set of packet commitments once a quorum of that set has signed it.`}),`\n`,_jsxs(_components.p,{children:[`The Go implementation is the package `,_jsx(_components.code,{children:`attestations`}),` in `,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/attestations`,children:_jsx(_components.code,{children:`modules/light-clients/attestations`})}),`, imported as:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`go`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-high-contrast github-dark-high-contrast`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0a0c10`,color:`#0e1116`,\"--shiki-dark\":`#f0f3f6`},language:`go`,children:_jsxs(_components.code,{language:`go`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`import`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` ibcattestations `}),_jsx(_components.span,{style:{color:`#032563`,\"--shiki-dark\":`#ADDCFF`},children:`\"`}),_jsx(_components.span,{style:{color:`#702C00`,\"--shiki-dark\":`#FFB757`},children:`github.com/cosmos/ibc-go/v11/modules/light-clients/attestations`}),_jsx(_components.span,{style:{color:`#032563`,\"--shiki-dark\":`#ADDCFF`},children:`\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`For the protocol-level description — what attestors are, how proofs reach a chain — see `,_jsx(_components.a,{href:`/ibc/04-light-clients/01-attestation-light-client`,children:`the attestation light client`}),`. For the EVM implementation, see `,_jsx(_components.a,{href:`/ibc/06-ibc-solidity-contracts/05-attestation-light-client`,children:`attestation light client contracts`}),`.`]}),`\n`,_jsx(Warning,{children:_jsx(_components.p,{children:`This package is EXPERIMENTAL and is not yet stable. It may change in backwards-incompatible ways without notice.`})}),`\n`,_jsx(Heading,{level:`2`,id:`trust-model`,children:`Trust model`}),`\n`,_jsxs(_components.p,{children:[`The attestor set and the quorum threshold are fixed at client creation. `,_jsx(_components.code,{children:`ClientState.Validate`}),` (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/client_state.go`,children:_jsx(_components.code,{children:`client_state.go`})}),`) rejects a client state whose attestor list is empty, whose `,_jsx(_components.code,{children:`MinRequiredSigs`}),` is zero or exceeds the number of attestors, or whose attestor list contains an empty address, an address that is not a hex address, or a duplicate after normalization.`]}),`\n`,_jsx(_components.p,{children:`There is no mechanism to add, remove or rotate attestors afterwards. The only state transition available to the client beyond adding consensus states is being frozen.`}),`\n`,_jsx(Heading,{level:`2`,id:`state`,children:`State`}),`\n`,_jsx(Heading,{level:`3`,id:`clientstate`,children:_jsx(_components.code,{children:`ClientState`})}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Field`}),_jsx(_components.th,{children:`Go type`}),_jsx(_components.th,{children:`Description`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`AttestorAddresses`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`[]string`})}),_jsxs(_components.td,{children:[`Fixed set of attestor addresses, hex, compared after `,_jsx(_components.code,{children:`common.HexToAddress`}),` normalization`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`MinRequiredSigs`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`uint32`})}),_jsx(_components.td,{children:`Quorum: the minimum number of signatures a proof must carry`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`LatestHeight`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`uint64`})}),_jsx(_components.td,{children:`Highest attested height; the revision number is always 0`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`IsFrozen`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`bool`})}),_jsxs(_components.td,{children:[`When true, updates and proof verification are refused with `,_jsx(_components.code,{children:`ErrClientFrozen`})]})]})]})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`NewClientState(attestorAddresses, minRequiredSigs, latestHeight)`}),` constructs one with `,_jsx(_components.code,{children:`IsFrozen`}),` false. `,_jsx(_components.code,{children:`ClientType()`}),` returns `,_jsx(_components.code,{children:`exported.Attestations`}),`.`]}),`\n`,_jsx(Heading,{level:`3`,id:`consensusstate`,children:_jsx(_components.code,{children:`ConsensusState`})}),`\n`,_jsxs(_components.p,{children:[`One field, `,_jsx(_components.code,{children:`Timestamp uint64`}),`, in nanoseconds. `,_jsx(_components.code,{children:`ValidateBasic`}),` rejects a zero timestamp. `,_jsx(_components.code,{children:`GetTimestamp()`}),` is deprecated and panics; read the timestamp through `,_jsx(_components.code,{children:`LightClientModule.TimestampAtHeight`}),`, which returns the stored nanosecond value (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/consensus_state.go`,children:_jsx(_components.code,{children:`consensus_state.go`})}),`).`]}),`\n`,_jsxs(_components.p,{children:[`Consensus states are stored per height under the standard `,_jsx(_components.code,{children:`host.ConsensusStateKey`}),` in the client’s prefixed store (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/store.go`,children:_jsx(_components.code,{children:`store.go`})}),`).`]}),`\n`,_jsx(Heading,{level:`2`,id:`the-client-message`,children:`The client message`}),`\n`,_jsxs(_components.p,{children:[`Both client updates and proofs use the same envelope, `,_jsx(_components.code,{children:`AttestationProof`}),`, which implements `,_jsx(_components.code,{children:`exported.ClientMessage`}),`:`]}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Field`}),_jsx(_components.th,{children:`Go type`}),_jsx(_components.th,{children:`Description`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`AttestationData`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`[]byte`})}),_jsxs(_components.td,{children:[`ABI-encoded `,_jsx(_components.code,{children:`StateAttestation`}),` or `,_jsx(_components.code,{children:`PacketAttestation`})]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`Signatures`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`[][]byte`})}),_jsxs(_components.td,{children:[`Each exactly `,_jsx(_components.code,{children:`SignatureLength`}),` (65) bytes, `,_jsx(_components.code,{children:`r || s || v`})]})]})]})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`ValidateBasic`}),` (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/attestation_proof.go`,children:_jsx(_components.code,{children:`attestation_proof.go`})}),`) requires non-empty attestation data, requires the data to decode as either a `,_jsx(_components.code,{children:`PacketAttestation`}),` with at least one packet or a `,_jsx(_components.code,{children:`StateAttestation`}),`, requires at least one signature, and requires each signature to be 65 bytes.`]}),`\n`,_jsxs(_components.p,{children:[`The two attestation payloads are Go structs in `,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/abi.go`,children:_jsx(_components.code,{children:`abi.go`})}),`:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`14`,language:`go`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-high-contrast github-dark-high-contrast`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0a0c10`,color:`#0e1116`,\"--shiki-dark\":`#f0f3f6`},language:`go`,children:_jsxs(_components.code,{language:`go`,numberOfLines:`14`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`type`}),_jsx(_components.span,{style:{color:`#702C00`,\"--shiki-dark\":`#FFB757`},children:` StateAttestation`}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:` struct`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` Height `}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`uint64`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` Timestamp `}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`uint64`}),_jsx(_components.span,{style:{color:`#66707B`,\"--shiki-dark\":`#BDC4CC`},children:` // nanoseconds in Go; seconds on the wire`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`type`}),_jsx(_components.span,{style:{color:`#702C00`,\"--shiki-dark\":`#FFB757`},children:` PacketAttestation`}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:` struct`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` Height `}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`uint64`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` Packets []`}),_jsx(_components.span,{style:{color:`#702C00`,\"--shiki-dark\":`#FFB757`},children:`PacketCompact`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`type`}),_jsx(_components.span,{style:{color:`#702C00`,\"--shiki-dark\":`#FFB757`},children:` PacketCompact`}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:` struct`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` Path []`}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`byte`}),_jsx(_components.span,{style:{color:`#66707B`,\"--shiki-dark\":`#BDC4CC`},children:` // keccak256 of the commitment path, 32 bytes`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` Commitment []`}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`byte`}),_jsx(_components.span,{style:{color:`#66707B`,\"--shiki-dark\":`#BDC4CC`},children:` // raw 32-byte commitment; 32 zero bytes means absent`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`}`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`StateAttestation`}),` is ABI-encoded as two `,_jsx(_components.code,{children:`uint64`}),` arguments, `,_jsx(_components.code,{children:`(height, timestamp)`}),`. `,_jsx(_components.code,{children:`PacketAttestation`}),` is encoded as a single tuple argument holding a `,_jsx(_components.code,{children:`uint64`}),` height and a dynamic array of `,_jsx(_components.code,{children:`(bytes32 path, bytes32 commitment)`}),` tuples. The encoding is what makes the same attestor output usable by the EVM implementation; see `,_jsx(_components.a,{href:`/ibc/06-ibc-solidity-contracts/05-attestation-light-client`,children:`attestation light client contracts`}),` for that side. On the Go side it is `,_jsx(_components.code,{children:`go-ethereum`}),`’s `,_jsx(_components.code,{children:`accounts/abi`}),` package and nothing more.`]}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[_jsx(_components.code,{children:`StateAttestation.ABIEncode`}),` divides the timestamp by 10`,_jsx(`sup`,{children:`9`}),` and `,_jsx(_components.code,{children:`ABIDecodeStateAttestation`}),` multiplies it back. The wire value is seconds; the value stored in the consensus state is nanoseconds. Sub-second precision does not survive a round trip.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`signature-verification`,children:`Signature verification`}),`\n`,_jsxs(_components.p,{children:[`Signatures do not cover the attestation data directly. `,_jsx(_components.code,{children:`TaggedSigningInput`}),` (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/signature.go`,children:_jsx(_components.code,{children:`signature.go`})}),`) computes:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-high-contrast github-dark-high-contrast`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0a0c10`,color:`#0e1116`,\"--shiki-dark\":`#f0f3f6`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`digest = sha256( typeTag || sha256(attestationData) )`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`where `,_jsx(_components.code,{children:`typeTag`}),` is one byte: `,_jsx(_components.code,{children:`AttestationTypeState`}),` (`,_jsx(_components.code,{children:`0x01`}),`) for client updates, `,_jsx(_components.code,{children:`AttestationTypePacket`}),` (`,_jsx(_components.code,{children:`0x02`}),`) for membership and non-membership proofs. The tag is what stops a packet attestation being replayed as a state attestation.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`ClientState.verifySignatures`}),` then, for the proof’s signatures:`]}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[`refuses an empty signature list, and refuses a list shorter than `,_jsx(_components.code,{children:`MinRequiredSigs`}),`;`]}),`\n`,_jsx(_components.li,{children:`refuses any signature that is not 65 bytes;`}),`\n`,_jsxs(_components.li,{children:[`normalizes the recovery byte — 27 becomes 0, 28 becomes 1, anything else is left alone — and recovers the signer with `,_jsx(_components.code,{children:`crypto.SigToPub`}),`;`]}),`\n`,_jsxs(_components.li,{children:[`refuses a repeated signer (`,_jsx(_components.code,{children:`ErrDuplicateSigner`}),`);`]}),`\n`,_jsxs(_components.li,{children:[`refuses a signer outside the attestor set (`,_jsx(_components.code,{children:`ErrUnknownSigner`}),`).`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`Quorum is counted as the number of signatures supplied, not the number that verified — every signature in the list must recover to a distinct attestor, so the two coincide, but a proof carrying extra junk signatures is rejected outright rather than ignored.`}),`\n`,_jsx(Heading,{level:`2`,id:`client-updates`,children:`Client updates`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`VerifyClientMessage`}),` refuses a frozen client, refuses a client message that is not an `,_jsx(_components.code,{children:`*AttestationProof`}),`, and verifies the signatures against the `,_jsx(_components.code,{children:`0x01`}),` tag. It does not inspect the height or the timestamp.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`UpdateState`}),` (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/update.go`,children:_jsx(_components.code,{children:`update.go`})}),`) then decodes the `,_jsx(_components.code,{children:`StateAttestation`}),`, writes a `,_jsx(_components.code,{children:`ConsensusState`}),` at `,_jsx(_components.code,{children:`clienttypes.NewHeight(0, stateAttestation.Height)`}),`, raises `,_jsx(_components.code,{children:`LatestHeight`}),` if the attested height is greater, and stores the client state. An update may therefore write a consensus state at a height at or below `,_jsx(_components.code,{children:`LatestHeight`}),`.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`UpdateState`}),` panics rather than returning an error on a wrong client message type or undecodable data, on the assumption that `,_jsx(_components.code,{children:`VerifyClientMessage`}),` ran first.`]}),`\n`,_jsx(Heading,{level:`3`,id:`misbehaviour`,children:`Misbehaviour`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`CheckForMisbehaviour`}),` decodes the state attestation, looks up the consensus state already stored at that height, and returns true when one exists with a different timestamp. `,_jsx(_components.code,{children:`UpdateStateOnMisbehaviour`}),` sets `,_jsx(_components.code,{children:`IsFrozen`}),` and writes the client state back; nothing unfreezes it.`]}),`\n`,_jsx(_components.p,{children:`Note that a consensus state is written at a height only once a timestamp has been attested for it. Resubmitting the same height with the same timestamp is not misbehaviour.`}),`\n`,_jsx(Heading,{level:`2`,id:`proof-verification`,children:`Proof verification`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`verifyMembership`}),` and `,_jsx(_components.code,{children:`verifyNonMembership`}),` (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/client_state.go`,children:_jsx(_components.code,{children:`client_state.go`})}),`) share their preconditions. Both refuse a frozen client and an empty path, both require a consensus state to already exist at the proof height, both unmarshal the `,_jsx(_components.code,{children:`proof`}),` bytes as a protobuf `,_jsx(_components.code,{children:`AttestationProof`}),`, both verify signatures against the `,_jsx(_components.code,{children:`0x02`}),` tag, both require `,_jsx(_components.code,{children:`packetAttestation.Height`}),` to equal the proof height’s revision height, and both require a non-empty packet list.`]}),`\n`,_jsxs(_components.p,{children:[`The path must be a `,_jsx(_components.code,{children:`commitmenttypesv2.MerklePath`}),` with exactly one key element, and the comparison key is `,_jsx(_components.code,{children:`crypto.Keccak256(merklePath.KeyPath[0])`}),`.`]}),`\n`,_jsx(Heading,{level:`3`,id:`membership`,children:`Membership`}),`\n`,_jsxs(_components.p,{children:[`The `,_jsx(_components.code,{children:`value`}),` must be exactly 32 bytes. Verification scans the attested packets for an entry whose `,_jsx(_components.code,{children:`Path`}),` and `,_jsx(_components.code,{children:`Commitment`}),` are both 32 bytes and equal to the hashed path and the value respectively. No match is `,_jsx(_components.code,{children:`ErrNotMember`}),`.`]}),`\n`,_jsx(Heading,{level:`3`,id:`non-membership`,children:`Non-membership`}),`\n`,_jsxs(_components.p,{children:[`Verification scans for entries whose `,_jsx(_components.code,{children:`Path`}),` matches the hashed path. If none match, the result is `,_jsx(_components.code,{children:`ErrNotMember`}),` — absence from the attested list does not prove absence on the counterparty. If entries match but any of them has a commitment that is not 32 zero bytes, the result is `,_jsx(_components.code,{children:`ErrNonMembershipFailed`}),`. The attestors have to assert the empty path explicitly.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`delayTimePeriod`}),` and `,_jsx(_components.code,{children:`delayBlockPeriod`}),` are accepted by the `,_jsx(_components.code,{children:`LightClientModule`}),` methods and not used.`]}),`\n`,_jsx(Heading,{level:`2`,id:`client-status`,children:`Client status`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`Status`}),` (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/light_client_module.go`,children:_jsx(_components.code,{children:`light_client_module.go`})}),`):`]}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Status`}),_jsx(_components.th,{children:`Condition`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`exported.Unknown`})}),_jsx(_components.td,{children:`no client state found for the client ID`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`exported.Frozen`})}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`IsFrozen`}),` is true`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`exported.Active`})}),_jsx(_components.td,{children:`otherwise`})]})]})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`LatestHeight`}),` returns `,_jsx(_components.code,{children:`clienttypes.NewHeight(0, clientState.LatestHeight)`}),`, or a zero height when the client state is missing.`]}),`\n`,_jsx(Heading,{level:`2`,id:`what-the-module-does-not-support`,children:`What the module does not support`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`RecoverClient`}),` returns `,_jsx(_components.code,{children:`ibcerrors.ErrInvalidRequest`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`VerifyUpgradeAndUpdateState`}),` returns `,_jsx(_components.code,{children:`clienttypes.ErrInvalidUpgradeClient`}),`.`]}),`\n`,_jsx(_components.li,{children:`No attestor rotation: the set is fixed at creation.`}),`\n`,_jsx(_components.li,{children:`Revision numbers are always 0; only the revision height is meaningful.`}),`\n`,_jsxs(_components.li,{children:[`Genesis is a no-op: `,_jsx(_components.code,{children:`DefaultGenesis`}),` returns nil and `,_jsx(_components.code,{children:`ValidateGenesis`}),` always succeeds (`,_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/module.go`,children:_jsx(_components.code,{children:`module.go`})}),`).`]}),`\n`,_jsxs(_components.li,{children:[`No module-level CLI: `,_jsx(_components.code,{children:`GetTxCmd`}),` and `,_jsx(_components.code,{children:`GetQueryCmd`}),` both return nil. Use the 02-client commands.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`integration`,children:`Integration`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`NewLightClientModule(cdc, storeProvider)`}),` returns a `,_jsx(_components.code,{children:`LightClientModule`}),` implementing `,_jsx(_components.code,{children:`exported.LightClientModule`}),`. Register it with the client keeper under `,_jsx(_components.code,{children:`ibcattestations.ModuleName`}),` (`,_jsx(_components.code,{children:`attestations`}),`), which becomes the client type prefix:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`go`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-high-contrast github-dark-high-contrast`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0a0c10`,color:`#0e1116`,\"--shiki-dark\":`#f0f3f6`},language:`go`,children:_jsxs(_components.code,{language:`go`,numberOfLines:`5`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`import`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` ibcattestations `}),_jsx(_components.span,{style:{color:`#032563`,\"--shiki-dark\":`#ADDCFF`},children:`\"`}),_jsx(_components.span,{style:{color:`#702C00`,\"--shiki-dark\":`#FFB757`},children:`github.com/cosmos/ibc-go/v11/modules/light-clients/attestations`}),_jsx(_components.span,{style:{color:`#032563`,\"--shiki-dark\":`#ADDCFF`},children:`\"`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`storeProvider `}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`:=`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` app.IBCKeeper.ClientKeeper.`}),_jsx(_components.span,{style:{color:`#622CBC`,\"--shiki-dark\":`#DBB7FF`},children:`GetStoreProvider`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`()`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`attestationsLightClientModule `}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`:=`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:` ibcattestations.`}),_jsx(_components.span,{style:{color:`#622CBC`,\"--shiki-dark\":`#DBB7FF`},children:`NewLightClientModule`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`(appCodec, storeProvider)`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`app.IBCKeeper.ClientKeeper.`}),_jsx(_components.span,{style:{color:`#622CBC`,\"--shiki-dark\":`#DBB7FF`},children:`AddRoute`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`(ibcattestations.ModuleName, `}),_jsx(_components.span,{style:{color:`#A0111F`,\"--shiki-dark\":`#FF9492`},children:`\u0026`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`attestationsLightClientModule)`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Register the module’s concrete types so core IBC can unmarshal them from `,_jsx(_components.code,{children:`Any`}),`:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`go`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-high-contrast github-dark-high-contrast`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0a0c10`,color:`#0e1116`,\"--shiki-dark\":`#f0f3f6`},language:`go`,children:_jsxs(_components.code,{language:`go`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`ibcattestations.`}),_jsx(_components.span,{style:{color:`#622CBC`,\"--shiki-dark\":`#DBB7FF`},children:`RegisterInterfaces`}),_jsx(_components.span,{style:{color:`#0E1116`,\"--shiki-dark\":`#F0F3F6`},children:`(registry)`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`AppModuleBasic.RegisterInterfaces`}),` does this for you if you add `,_jsx(_components.code,{children:`ibcattestations.NewAppModule(attestationsLightClientModule)`}),` to the module manager.`]}),`\n`,_jsx(Heading,{level:`3`,id:`creating-a-client`,children:`Creating a client`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`Initialize`}),` unmarshals the client state and the consensus state, runs `,_jsx(_components.code,{children:`Validate`}),` and `,_jsx(_components.code,{children:`ValidateBasic`}),` on them, and writes the consensus state at `,_jsx(_components.code,{children:`clienttypes.NewHeight(0, clientState.LatestHeight)`}),` — so the `,_jsx(_components.code,{children:`LatestHeight`}),` in the initial client state and the timestamp in the initial consensus state must describe the same height, and that height is the only one a proof can be verified against until an update lands.`]}),`\n`,_jsx(Heading,{level:`2`,id:`errors`,children:`Errors`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.a,{href:`https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/attestations/errors.go`,children:_jsx(_components.code,{children:`errors.go`})}),` registers, under codespace `,_jsx(_components.code,{children:`attestations`}),`: `,_jsx(_components.code,{children:`ErrInvalidAttestationProof`}),`, `,_jsx(_components.code,{children:`ErrInvalidSignature`}),`, `,_jsx(_components.code,{children:`ErrInvalidQuorum`}),`, `,_jsx(_components.code,{children:`ErrDuplicateSigner`}),`, `,_jsx(_components.code,{children:`ErrUnknownSigner`}),`, `,_jsx(_components.code,{children:`ErrInvalidAttestationData`}),`, `,_jsx(_components.code,{children:`ErrInvalidHeight`}),`, `,_jsx(_components.code,{children:`ErrInvalidTimestamp`}),`, `,_jsx(_components.code,{children:`ErrClientFrozen`}),`, `,_jsx(_components.code,{children:`ErrNotMember`}),`, `,_jsx(_components.code,{children:`ErrInvalidPath`}),`, `,_jsx(_components.code,{children:`ErrInvalidValue`}),`, `,_jsx(_components.code,{children:`ErrProcessedTimeNotFound`}),`, `,_jsx(_components.code,{children:`ErrProcessedHeightNotFound`}),`, `,_jsx(_components.code,{children:`ErrDelayPeriodNotPassed`}),`, `,_jsx(_components.code,{children:`ErrNonMembershipFailed`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`see-also`,children:`See also`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/ibc/04-light-clients/01-attestation-light-client`,children:`The attestation light client`}),` — the protocol-level description`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/ibc/06-ibc-solidity-contracts/05-attestation-light-client`,children:`Attestation light client contracts`}),` — the EVM implementation`]}),`\n`,_jsx(_components.li,{children:_jsx(_components.a,{href:`/ibc-go/latest/light-clients/developer-guide/overview`,children:`Light client developer guide`})}),`\n`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"codeStyling":{"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}},"config":{},"pageMetadata":{"title":"The attestation light client","description":"How the ibc-go attestation light client verifies state a quorum of attestors has signed.","href":"/ibc-go/latest/light-clients/attestation/attestation-light-client"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Overview","slug":"overview","depth":2,"children":[]},{"title":"Trust model","slug":"trust-model","depth":2,"children":[]},{"title":"State","slug":"state","depth":2,"children":[{"title":"ClientState","slug":"clientstate","depth":3,"children":[]},{"title":"ConsensusState","slug":"consensusstate","depth":3,"children":[]}]},{"title":"The client message","slug":"the-client-message","depth":2,"children":[]},{"title":"Signature verification","slug":"signature-verification","depth":2,"children":[]},{"title":"Client updates","slug":"client-updates","depth":2,"children":[{"title":"Misbehaviour","slug":"misbehaviour","depth":3,"children":[]}]},{"title":"Proof verification","slug":"proof-verification","depth":2,"children":[{"title":"Membership","slug":"membership","depth":3,"children":[]},{"title":"Non-membership","slug":"non-membership","depth":3,"children":[]}]},{"title":"Client status","slug":"client-status","depth":2,"children":[]},{"title":"What the module does not support","slug":"what-the-module-does-not-support","depth":2,"children":[]},{"title":"Integration","slug":"integration","depth":2,"children":[{"title":"Creating a client","slug":"creating-a-client","depth":3,"children":[]}]},{"title":"Errors","slug":"errors","depth":2,"children":[]},{"title":"See also","slug":"see-also","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":["/docs/_mintlify/custom/ibc-docs/1efa1b9f25769b22/custom.js"],"subdomain":"ibc-docs","siteName":"IBC Documentation","title":"The attestation light client - IBC Documentation","navMaps":{"firstHrefInVersion":{"latest":{"href":"/ibc-go/latest/light-clients/attestation/attestation-light-client","matchLevel":0},"v10.1.x":{"href":"/ibc-go/v10.1.x/light-clients/developer-guide/overview","matchLevel":2}},"firstHrefInLanguage":{}},"lastModified":"2026-10-29T18:43:19.157Z","originalFileLocation":"ibc-go/latest/light-clients/attestation/attestation-light-client.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"How the ibc-go attestation light client verifies state a quorum of attestors has signed.\">\u003cmeta data-mint-head name=\"application-name\" content=\"IBC Documentation\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"The attestation light client - IBC Documentation\">\u003cmeta data-mint-head property=\"og:description\" content=\"How the ibc-go attestation light client verifies state a quorum of attestors has signed.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"IBC Documentation\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://ibc-docs.mintlify.app/_mintlify/api/og/v2?division=Attestation\u0026amp;title=The+attestation+light+client\u0026amp;description=How+the+ibc-go+attestation+light+client+verifies+state+a+quorum+of+attestors+has+signed.\u0026amp;theme=83cf7571a86e66a217192c59\u0026amp;signature=2313fe83d7cbe5028ff0c05092657b2564f20537763c9ce97fb631aaf184186c\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"The attestation light client - IBC Documentation\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"How the ibc-go attestation light client verifies state a quorum of attestors has signed.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://ibc-docs.mintlify.app/_mintlify/api/og/v2?division=Attestation\u0026amp;title=The+attestation+light+client\u0026amp;description=How+the+ibc-go+attestation+light+client+verifies+state+a+quorum+of+attestors+has+signed.\u0026amp;theme=83cf7571a86e66a217192c59\u0026amp;signature=2313fe83d7cbe5028ff0c05092657b2564f20537763c9ce97fb631aaf184186c\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/docs/mintlify-assets/_mintlify/favicons/ibc-docs/M8QbcV1CzT5u-Qmv/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"IBC Documentation\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#000000\">\u003clink data-mint-head rel=\"canonical\" href=\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/docs/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/docs/ibc-go/latest/light-clients/attestation/attestation-light-client.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://ibc-docs.mintlify.dev/#organization\",\"name\":\"IBC Documentation\",\"url\":\"https://ibc-docs.mintlify.dev\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/ibc-docs/_IzGrVOELf0DsHtq/mintlify/assets/brand/ibc-wordmark-dark.svg?fit=max\u0026auto=format\u0026n=_IzGrVOELf0DsHtq\u0026q=85\u0026s=dd69509f8e278637cefba78620bc6f19\"}},{\"@type\":\"WebSite\",\"@id\":\"https://ibc-docs.mintlify.dev/docs#website\",\"name\":\"IBC Documentation\",\"url\":\"https://ibc-docs.mintlify.dev/docs\",\"publisher\":{\"@id\":\"https://ibc-docs.mintlify.dev/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client#webpage\",\"url\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client\",\"name\":\"The attestation light client\",\"description\":\"How the ibc-go attestation light client verifies state a quorum of attestors has signed.\",\"dateModified\":\"2026-10-29T18:43:19.157Z\",\"isPartOf\":{\"@id\":\"https://ibc-docs.mintlify.dev/docs#website\"},\"breadcrumb\":{\"@id\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"IBC Light Clients\",\"item\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/developer-guide/overview\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"The attestation light client\",\"item\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client#article\",\"headline\":\"The attestation light client\",\"name\":\"The attestation light client\",\"description\":\"How the ibc-go attestation light client verifies state a quorum of attestors has signed.\",\"url\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client\",\"mainEntityOfPage\":{\"@id\":\"https://ibc-docs.mintlify.dev/docs/ibc-go/latest/light-clients/attestation/attestation-light-client#webpage\"},\"dateModified\":\"2026-10-29T18:43:19.157Z\",\"publisher\":{\"@id\":\"https://ibc-docs.mintlify.dev/#organization\"},\"isPartOf\":{\"@id\":\"https://ibc-docs.mintlify.dev/docs#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"IBC Documentation\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"IBC Documentation","nav":{"global":null,"tabs":[{"tab":"Learn","pages":[{"title":"Learn","href":""}]},{"tab":"IBC CLI","pages":[{"title":"IBC CLI","href":"/ibc/05-ibc-cli/01-overview"}]},{"tab":"IBC-Solidity","pages":[{"title":"IBC-Solidity","href":"/ibc/06-ibc-solidity-contracts/01-overview"}]},{"tab":"IBC-Go","versions":[{"version":"latest","groups":[{"group":"Introduction","icon":"book-open","pages":[{"title":"IBC Documentation","href":"/ibc-go/latest/intro"}]},{"group":"IBC Core","icon":"box","pages":[{"title":"Overview","href":"/ibc-go/latest/ibc/overview"},{"group":"Core Concepts","icon":"lightbulb","pages":[{"title":"Best Practices","href":"/ibc-go/latest/ibc/best-practices"},{"title":"Integration","href":"/ibc-go/latest/ibc/integration"},{"title":"Permissioning","href":"/ibc-go/latest/ibc/permissioning"},{"title":"Relayer","href":"/ibc-go/latest/ibc/relayer"}]},{"group":"Apps","icon":"app-window","pages":[{"title":"IBC v2 Applications","href":"/ibc-go/latest/ibc/apps/ibcv2apps"},{"title":"IBC Applications","href":"/ibc-go/latest/ibc/apps/apps"},{"title":"Implement IBCModule interface and callbacks","href":"/ibc-go/latest/ibc/apps/ibcmodule"},{"title":"Bind ports","href":"/ibc-go/latest/ibc/apps/bindports"},{"title":"Keeper","href":"/ibc-go/latest/ibc/apps/keeper"},{"title":"Define packets and acks","href":"/ibc-go/latest/ibc/apps/packets_acks"},{"title":"Routing","href":"/ibc-go/latest/ibc/apps/routing"},{"title":"Custom Address Codec","href":"/ibc-go/latest/ibc/apps/address-codec"}]},{"group":"Middleware","icon":"layers","pages":[{"title":"IBC middleware","href":"/ibc-go/latest/ibc/middleware/overview"},{"title":"Create a custom IBC middleware","href":"/ibc-go/latest/ibc/middleware/develop"},{"title":"Create and integrate IBC v2 middleware","href":"/ibc-go/latest/ibc/middleware/developIBCv2"},{"title":"Integrating IBC middleware into a chain","href":"/ibc-go/latest/ibc/middleware/integration"}]},{"group":"Upgrades","icon":"arrow-up","pages":[{"title":"Upgrading IBC Chains Overview","href":"/ibc-go/latest/ibc/upgrades/intro"},{"title":"How to Upgrade IBC Chains and their Clients","href":"/ibc-go/latest/ibc/upgrades/quick-guide"},{"title":"IBC Client Developer Guide to Upgrades","href":"/ibc-go/latest/ibc/upgrades/developer-guide"},{"title":"Genesis Restart Upgrades","href":"/ibc-go/latest/ibc/upgrades/genesis-restart"}]}]},{"group":"IBC Migrations","icon":"arrow-right-left","pages":[{"group":"Version Upgrades","icon":"git-branch","pages":[{"title":"IBC-Go v11.0 to v11.1","href":"/ibc-go/latest/migrations/v11-to-v11-1"},{"title":"IBC-Go v10 to v11.1","href":"/ibc-go/latest/migrations/v10-to-v11-1"},{"title":"IBC-Go v10 to v11","href":"/ibc-go/latest/migrations/v10-to-v11"},{"title":"IBC-Go v8.1 to v10","href":"/ibc-go/latest/migrations/v8_1-to-v10"},{"title":"IBC-Go v8 to v8.1","href":"/ibc-go/latest/migrations/v8-to-v8_1"},{"title":"IBC-Go v7 to v8","href":"/ibc-go/latest/migrations/v7-to-v8"},{"title":"IBC-Go v7.2 to v7.3","href":"/ibc-go/latest/migrations/v7_2-to-v7_3"},{"title":"IBC-Go v7 to v7.1","href":"/ibc-go/latest/migrations/v7-to-v7_1"},{"title":"IBC-Go v6 to v7","href":"/ibc-go/latest/migrations/v6-to-v7"},{"title":"IBC-Go v5 to v6","href":"/ibc-go/latest/migrations/v5-to-v6"},{"title":"IBC-Go v4 to v5","href":"/ibc-go/latest/migrations/v4-to-v5"},{"title":"IBC-Go v3 to v4","href":"/ibc-go/latest/migrations/v3-to-v4"},{"title":"IBC-Go v2 to v3","href":"/ibc-go/latest/migrations/v2-to-v3"},{"title":"IBC-Go v1 to v2","href":"/ibc-go/latest/migrations/v1-to-v2"}]},{"group":"Additional Migration Guides","icon":"map","pages":[{"title":"SDK v0.43 to IBC-Go v1","href":"/ibc-go/latest/migrations/sdk-to-v1"},{"title":"Support transfer of coins whose base denom contains slashes","href":"/ibc-go/latest/migrations/support-denoms-with-slashes"},{"title":"Support the new StackBuilder primitive for Wiring Middlewares in the chain application","href":"/ibc-go/latest/migrations/support-stackbuilder"}]}]},{"group":"IBC Light Clients","icon":"shield-check","pages":[{"group":"Developer Guide","icon":"code","pages":[{"title":"Overview","href":"/ibc-go/latest/light-clients/developer-guide/overview"},{"title":"Light Client Module interface","href":"/ibc-go/latest/light-clients/developer-guide/light-client-module"},{"title":"Client State interface","href":"/ibc-go/latest/light-clients/developer-guide/client-state"},{"title":"Consensus State interface","href":"/ibc-go/latest/light-clients/developer-guide/consensus-state"},{"title":"Handling Updates and Misbehaviour","href":"/ibc-go/latest/light-clients/developer-guide/updates-and-misbehaviour"},{"title":"Handling Upgrades","href":"/ibc-go/latest/light-clients/developer-guide/upgrades"},{"title":"Existence/Non-Existence Proofs","href":"/ibc-go/latest/light-clients/developer-guide/proofs"},{"title":"Handling Proposals","href":"/ibc-go/latest/light-clients/developer-guide/proposals"},{"title":"Setup","href":"/ibc-go/latest/light-clients/developer-guide/setup"},{"title":"Governance Proposals","description":null,"href":"/ibc-go/latest/light-clients/proposals"}]},{"group":"Attestation","icon":"badge-check","pages":[{"title":"The attestation light client","href":"/ibc-go/latest/light-clients/attestation/attestation-light-client"}]},{"group":"Localhost","icon":"monitor","pages":[{"title":"Overview","description":null,"href":"/ibc-go/latest/light-clients/localhost/overview"},{"title":"Integration","href":"/ibc-go/latest/light-clients/localhost/integration"},{"title":"ClientState","href":"/ibc-go/latest/light-clients/localhost/client-state"},{"title":"Connection","href":"/ibc-go/latest/light-clients/localhost/connection"},{"title":"State Verification","href":"/ibc-go/latest/light-clients/localhost/state-verification"}]},{"group":"Solo Machine","icon":"cpu","pages":[{"title":"Solomachine","href":"/ibc-go/latest/light-clients/solomachine/solomachine"},{"title":"Concepts","href":"/ibc-go/latest/light-clients/solomachine/concepts"},{"title":"State","href":"/ibc-go/latest/light-clients/solomachine/state"},{"title":"State Transitions","href":"/ibc-go/latest/light-clients/solomachine/state_transitions"}]},{"group":"Tendermint","icon":"boxes","pages":[{"title":"Overview","href":"/ibc-go/latest/light-clients/tendermint/overview"}]},{"group":"WASM","icon":"binary","pages":[{"title":"Overview","href":"/ibc-go/latest/light-clients/wasm/overview"},{"title":"Concepts","href":"/ibc-go/latest/light-clients/wasm/concepts"},{"title":"Integration","href":"/ibc-go/latest/light-clients/wasm/integration"},{"title":"Messages","href":"/ibc-go/latest/light-clients/wasm/messages"},{"title":"Governance","href":"/ibc-go/latest/light-clients/wasm/governance"},{"title":"Events","href":"/ibc-go/latest/light-clients/wasm/events"},{"title":"Contracts","href":"/ibc-go/latest/light-clients/wasm/contracts"},{"title":"Client","href":"/ibc-go/latest/light-clients/wasm/client"},{"title":"Migrations","href":"/ibc-go/latest/light-clients/wasm/migrations"}]}]},{"group":"IBC Apps","icon":"blocks","pages":[{"title":"General Message Passing (GMP)","href":"/ibc-go/latest/apps/gmp"},{"group":"Interchain Accounts","icon":"users","pages":[{"title":"Overview","href":"/ibc-go/latest/apps/interchain-accounts/overview"},{"title":"Development Use Cases","href":"/ibc-go/latest/apps/interchain-accounts/development"},{"title":"Authentication Modules","href":"/ibc-go/latest/apps/interchain-accounts/auth-modules"},{"title":"Integration","href":"/ibc-go/latest/apps/interchain-accounts/integration"},{"title":"Messages","href":"/ibc-go/latest/apps/interchain-accounts/messages"},{"title":"Parameters","href":"/ibc-go/latest/apps/interchain-accounts/parameters"},{"title":"Transaction Encoding","href":"/ibc-go/latest/apps/interchain-accounts/tx-encoding"},{"title":"Client","href":"/ibc-go/latest/apps/interchain-accounts/client"},{"title":"Active Channels","href":"/ibc-go/latest/apps/interchain-accounts/active-channels"},{"group":"Legacy","icon":"archive","pages":[{"title":"Authentication Modules","href":"/ibc-go/latest/apps/interchain-accounts/legacy/auth-modules"},{"title":"Integration","href":"/ibc-go/latest/apps/interchain-accounts/legacy/integration"},{"title":"Keeper API","href":"/ibc-go/latest/apps/interchain-accounts/legacy/keeper-api"}]}]},{"group":"Transfer","icon":"send","pages":[{"title":"Overview","href":"/ibc-go/latest/apps/transfer/overview"},{"title":"State","href":"/ibc-go/latest/apps/transfer/state"},{"title":"State Transitions","href":"/ibc-go/latest/apps/transfer/state-transitions"},{"title":"Messages","href":"/ibc-go/latest/apps/transfer/messages"},{"title":"Events","href":"/ibc-go/latest/apps/transfer/events"},{"title":"Metrics","href":"/ibc-go/latest/apps/transfer/metrics"},{"title":"Params","href":"/ibc-go/latest/apps/transfer/params"},{"title":"Authorizations","href":"/ibc-go/latest/apps/transfer/authorizations"},{"title":"Client","href":"/ibc-go/latest/apps/transfer/client"},{"title":"IBC v2 Transfer","href":"/ibc-go/latest/apps/transfer/IBCv2-transfer"}]}]},{"group":"IBC Middleware","icon":"layers","pages":[{"group":"Callbacks","icon":"repeat","pages":[{"title":"Overview","href":"/ibc-go/latest/middleware/callbacks/overview"},{"title":"Integration","href":"/ibc-go/latest/middleware/callbacks/integration"},{"title":"Interfaces","href":"/ibc-go/latest/middleware/callbacks/interfaces"},{"title":"Events","href":"/ibc-go/latest/middleware/callbacks/events"},{"title":"End Users","href":"/ibc-go/latest/middleware/callbacks/end-users"},{"title":"Gas Management","href":"/ibc-go/latest/middleware/callbacks/gas"},{"title":"Callbacks with IBC v2","href":"/ibc-go/latest/middleware/callbacks/callbacks-IBCv2"}]},{"group":"Packet Forward Middleware","icon":"forward","pages":[{"title":"Overview","href":"/ibc-go/latest/middleware/packet-forward-middleware/overview"},{"title":"Integration","href":"/ibc-go/latest/middleware/packet-forward-middleware/integration"},{"title":"Example Flows","href":"/ibc-go/latest/middleware/packet-forward-middleware/example-usage"}]},{"group":"Rate Limit Middleware","icon":"gauge","pages":[{"title":"Overview","href":"/ibc-go/latest/middleware/rate-limit-middleware/overview"},{"title":"Integration","href":"/ibc-go/latest/middleware/rate-limit-middleware/integration"},{"title":"Setting Rate Limits","href":"/ibc-go/latest/middleware/rate-limit-middleware/setting-limits"},{"title":"Migration","href":"/ibc-go/latest/middleware/rate-limit-middleware/migration"}]}]},{"group":"Security","icon":"lock","pages":[{"title":"Security Audits","href":"/ibc-go/latest/security-audits"}]},{"group":"Architecture Decision Records","icon":"scroll-text","pages":[{"title":"Architecture Decision Records (ADR)","href":"/ibc-go/latest/architecture/README"},{"title":"ADR 001: Coin Source Tracing","href":"/ibc-go/latest/architecture/adr-001-coin-source-tracing"},{"title":"ADR 002: Go Module Versioning","href":"/ibc-go/latest/architecture/adr-002-go-module-versioning"},{"title":"ADR 003: ICS-27 Acknowledgement Format","href":"/ibc-go/latest/architecture/adr-003-ics27-acknowledgement"},{"title":"ADR 004: Lock Fee Module Upon Escrow Out Of Balance","href":"/ibc-go/latest/architecture/adr-004-ics29-lock-fee-module"},{"title":"ADR 005: UpdateClient Events ClientState Consensus Heights","href":"/ibc-go/latest/architecture/adr-005-consensus-height-events"},{"title":"ADR 006: 02-Client Refactor","href":"/ibc-go/latest/architecture/adr-006-02-client-refactor"},{"title":"ADR 007: Solo Machine Sign Bytes","href":"/ibc-go/latest/architecture/adr-007-solomachine-signbytes"},{"title":"ADR 008: Callback to IBC Actors","href":"/ibc-go/latest/architecture/adr-008-app-caller-cbs"},{"title":"ADR 009: ICS27 Message Server Addition","href":"/ibc-go/latest/architecture/adr-009-v6-ics27-msgserver"},{"title":"ADR 010: IBC Light Clients As SDK Modules","href":"/ibc-go/latest/architecture/adr-010-light-clients-as-sdk-modules"},{"title":"ADR 011: ICS-20 Transfer State Entry For Total Amount Of Tokens In Escrow","href":"/ibc-go/latest/architecture/adr-011-transfer-total-escrow-state-entry"},{"title":"ADR 015: IBC Packet Receiver","href":"/ibc-go/latest/architecture/adr-015-ibc-packet-receiver"},{"title":"ADR 025: IBC Passive Channels","href":"/ibc-go/latest/architecture/adr-025-ibc-passive-channels"},{"title":"ADR 026: IBC Client Recovery Mechanisms","href":"/ibc-go/latest/architecture/adr-026-ibc-client-recovery-mechanisms"},{"title":"ADR 27: Add Support For Wasm Based Light Client","href":"/ibc-go/latest/architecture/adr-027-ibc-wasm"}]},{"group":"Changelog","icon":"rotate-ccw","pages":[{"title":"Changelog","mode":"wide","href":"/ibc-go/latest/changelog/release-notes"}]},{"group":"Repositories","icon":"folder-git-2","pages":[{"title":"cosmos/ibc-go","url":"https://github.com/cosmos/ibc-go","icon":"github","href":"/ibc-go/latest/repos/go-ibc-go"}]}]},{"version":"v10.1.x","pages":[{"title":"v10.1.x","href":"/ibc-go/v10.1.x/light-clients/developer-guide/overview"}]}]},{"tab":"Specification","pages":[{"title":"Specification","href":"/ibc/spec/ics-001-ics-standard/README"}]}]},"title":"The attestation light client - IBC Documentation","buildId":"dpl_DGmhKsAzJdVqypJzon18vViniWRY","deploymentId":"dpl_DGmhKsAzJdVqypJzon18vViniWRY","dynamics":[],"chunks":[]}