feat(v3): add GraftConfigTreeNode to materialize anonymous config tree nodes - #370
Merged
Conversation
pkieltyka
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
v3.GraftConfigTreeNode(tree, replacement)— swaps the first anonymous node-hash leaf (WalletConfigTreeNodeLeaf) whose image hash equalsreplacement.ImageHash()for the fullreplacementsubtree. The tree's image hash is unchanged by construction: a swap only happens on hash equality. Idempotent: if the hash is already materialized as a full subtree, the tree is returned unchanged withok=true.Why
BuildIntentConfigurationSignature/BuildRegularSignatureFromSignaturescan only attach a supplied signer signature to a full leaf. Configs decoded from keymachine can carry sapient leaves (e.g. the Trails timed-refund leaf) as anonymous node hashes — no address — so the supplied sapient signature silently contributes zero weight and the on-chain threshold fails.Both trails-api and trails-watchtower currently carry private copies of this graft (
graftSapientLeaf). This upstreams it generically: any subtree can be grafted, and hash-equality matching makes the substitution provably config-preserving without a separate verification step. Related: the commented-out reverse-directionreplaceSapientSignerWithNodeInConfigTreeinintent_config.go.Tests
core/v3/graft_test.go: value and pointer node leaves underWalletConfigTreeNode, node leaf underWalletConfigTreeNestedLeaf(Weight/Threshold preserved), already-materialized subtree, hash absent, nil inputs. Every graft asserts image-hash equality with the original tree and that the input tree is not mutated. Includes a non-sapient replacement to cover the generic case.