Summary
pkg/api.InclusionProofV2.Verify omits two checks performed by the JavaScript, Java, and Rust state-transition SDK verifiers:
- the state ID must belong to the shard named by the UC's
ShardTreeCertificate; and
- the UC seal network ID must equal the trust-base network ID.
As a result, the published Go verifier accepts a validly certified leaf from the wrong shard when the caller supplies that shard as VerifierContext.ExpectedShardID. It also accepts a seal whose NetworkID differs from TrustBase.GetNetworkID() when the same validator keys can satisfy the trust base.
This supersedes the Yellowpaper-driven remediation proposed in #183. The SDK implementations are the compatibility target here and do not require a partition-description or sharding-scheme input.
SDK behavior
All three SDKs independently enforce the same relation:
- derive the state ID from certification/transaction data;
- verify the SMT path against
UC.IR.h;
- require
UC.ShardTreeCertificate.Shard to be a prefix of the state ID;
- require
UC.UnicitySeal.NetworkID == TrustBase.NetworkID; and
- verify the UC root and quorum signatures.
Relevant implementations:
- JS:
src/transaction/verification/rule/InclusionProofVerificationRule.ts, ShardIdMatchesStateIdRule.ts, and api/bft/verification/UnicityCertificateVerifier.ts
- Java:
transaction/verification/InclusionProofVerificationRule.java, ShardIdMatchesStateIdRule.java, and api/bft/verification/UnicityCertificateVerification.java
- Rust:
src/verify/mod.rs::verify_inclusion_proof_for and verify_unicity_certificate
Current Go behavior
InclusionProofV2.Verify derives the SMT key from v2.StateID and verifies the path, then calls:
uc.Verify(vctx.TrustBase, crypto.SHA256, vctx.PartitionID, vctx.ExpectedShardID, vctx.ShardConfHash)
The BFT library checks only that the certificate shard equals the caller-supplied ExpectedShardID. It does not check that the shard prefix matches the state ID. UnicitySeal.Verify validates quorum signatures but does not compare the seal and trust-base network IDs.
The existing BFT sharding integration test derives ExpectedShardID from the endpoint that served the proof, so it does not cover a key committed under the wrong shard's otherwise valid UC.
Impact
The three state-transition SDKs are not affected. The affected surface is external users of the published Go pkg/api verifier.
In a multi-shard deployment, a faulty or Byzantine shard quorum can certify a state ID owned by another shard. A caller that uses the serving endpoint or UC to choose ExpectedShardID will accept the proof. Uniqueness is per shard tree, so the same state ID may then have accepted proofs in two shards.
The network mismatch is a domain-separation failure. It is exploitable when validator keys are reused across trust bases with different network IDs.
There are currently no non-test callers of InclusionProofV2.Verify inside this repository. Internal service paths use the deliberately local-only verifier in internal/proofverify/local.go.
Proposed fix
Bring the Go verifier into parity with the SDKs without changing VerifierContext:
- Validate that
v2.StateID is derived from the request certification data.
- Require the proof certification data fields used to derive the state ID to match the request.
- After decoding the UC, require
uc.ShardTreeCertificate.Shard.Comparator()(key).
- Require
uc.UnicitySeal.NetworkID == vctx.TrustBase.GetNetworkID().
- Retain
ExpectedShardID as an additional caller policy check for API compatibility.
- Add regression tests for a fully signed wrong-shard proof and for a signed seal with a mismatched network ID.
No full sharding scheme or partition descriptor is needed to match the SDK verifier behavior, and no public API break is required.
Related
Summary
pkg/api.InclusionProofV2.Verifyomits two checks performed by the JavaScript, Java, and Rust state-transition SDK verifiers:ShardTreeCertificate; andAs a result, the published Go verifier accepts a validly certified leaf from the wrong shard when the caller supplies that shard as
VerifierContext.ExpectedShardID. It also accepts a seal whoseNetworkIDdiffers fromTrustBase.GetNetworkID()when the same validator keys can satisfy the trust base.This supersedes the Yellowpaper-driven remediation proposed in #183. The SDK implementations are the compatibility target here and do not require a partition-description or sharding-scheme input.
SDK behavior
All three SDKs independently enforce the same relation:
UC.IR.h;UC.ShardTreeCertificate.Shardto be a prefix of the state ID;UC.UnicitySeal.NetworkID == TrustBase.NetworkID; andRelevant implementations:
src/transaction/verification/rule/InclusionProofVerificationRule.ts,ShardIdMatchesStateIdRule.ts, andapi/bft/verification/UnicityCertificateVerifier.tstransaction/verification/InclusionProofVerificationRule.java,ShardIdMatchesStateIdRule.java, andapi/bft/verification/UnicityCertificateVerification.javasrc/verify/mod.rs::verify_inclusion_proof_forandverify_unicity_certificateCurrent Go behavior
InclusionProofV2.Verifyderives the SMT key fromv2.StateIDand verifies the path, then calls:The BFT library checks only that the certificate shard equals the caller-supplied
ExpectedShardID. It does not check that the shard prefix matches the state ID.UnicitySeal.Verifyvalidates quorum signatures but does not compare the seal and trust-base network IDs.The existing BFT sharding integration test derives
ExpectedShardIDfrom the endpoint that served the proof, so it does not cover a key committed under the wrong shard's otherwise valid UC.Impact
The three state-transition SDKs are not affected. The affected surface is external users of the published Go
pkg/apiverifier.In a multi-shard deployment, a faulty or Byzantine shard quorum can certify a state ID owned by another shard. A caller that uses the serving endpoint or UC to choose
ExpectedShardIDwill accept the proof. Uniqueness is per shard tree, so the same state ID may then have accepted proofs in two shards.The network mismatch is a domain-separation failure. It is exploitable when validator keys are reused across trust bases with different network IDs.
There are currently no non-test callers of
InclusionProofV2.Verifyinside this repository. Internal service paths use the deliberately local-only verifier ininternal/proofverify/local.go.Proposed fix
Bring the Go verifier into parity with the SDKs without changing
VerifierContext:v2.StateIDis derived from the request certification data.uc.ShardTreeCertificate.Shard.Comparator()(key).uc.UnicitySeal.NetworkID == vctx.TrustBase.GetNetworkID().ExpectedShardIDas an additional caller policy check for API compatibility.No full sharding scheme or partition descriptor is needed to match the SDK verifier behavior, and no public API break is required.
Related