@@ -20,6 +20,8 @@ type Signer func(msg []byte) ([]byte, error)
2020
2121// SignLayoutB64 validates single-block layout, marshals to JSON, base64-encodes it,
2222// and signs the base64 payload, returning both the layout base64 and signature base64.
23+ //
24+ // Message signed = layoutB64 string (same as JS layoutBytesB64 if layout JSON matches).
2325func SignLayoutB64 (layout codec.Layout , signer Signer ) (layoutB64 string , layoutSigB64 string , err error ) {
2426 if len (layout .Blocks ) != 1 {
2527 return "" , "" , errors .New ("layout must contain exactly one block" )
@@ -40,27 +42,40 @@ func SignLayoutB64(layout codec.Layout, signer Signer) (layoutB64 string, layout
4042}
4143
4244// SignIndexB64 marshals the index to JSON, base64-encodes it, and signs the
43- // base64 payload, returning both the index base64 and creator-signature base64.
45+ // JSON string (not the base64), returning both the index base64 and creator-signature base64.
46+ //
47+ // IMPORTANT:
48+ // - Message signed = index JSON string (same as JS signArbitrary(indexFileString))
49+ // - indexB64 is still base64(JSON(index)), used in metadata and RQID generation.
4450func SignIndexB64 (idx IndexFile , signer Signer ) (indexB64 string , creatorSigB64 string , err error ) {
4551 raw , err := json .Marshal (idx )
4652 if err != nil {
4753 return "" , "" , errors .Errorf ("marshal index file: %w" , err )
4854 }
49- indexB64 = base64 .StdEncoding .EncodeToString (raw )
5055
51- sig , err := signer ([]byte (indexB64 ))
56+ indexJSON := string (raw )
57+
58+ // Sign the JSON string (JS-style)
59+ sig , err := signer ([]byte (indexJSON ))
5260 if err != nil {
5361 return "" , "" , errors .Errorf ("sign index: %w" , err )
5462 }
5563 creatorSigB64 = base64 .StdEncoding .EncodeToString (sig )
64+
65+ // Base64(JSON(index)) used as the first segment of indexSignatureFormat
66+ indexB64 = base64 .StdEncoding .EncodeToString (raw )
5667 return indexB64 , creatorSigB64 , nil
5768}
5869
5970// CreateSignatures produces the index signature format and index IDs:
6071//
61- // Base64(index_json). Base64(creator_signature)
72+ // indexSignatureFormat = Base64(index_json) + "." + Base64(creator_signature)
6273//
6374// It validates the layout has exactly one block.
75+ //
76+ // The "signer" can be:
77+ // - raw: directly sign msg bytes (legacy Go path)
78+ // - ADR-36: wrap msg into an ADR-36 sign doc, then sign (JS-compatible path)
6479func CreateSignatures (layout codec.Layout , signer Signer , ic , max uint32 ) (indexSignatureFormat string , indexIDs []string , err error ) {
6580 layoutB64 , layoutSigB64 , err := SignLayoutB64 (layout , signer )
6681 if err != nil {
@@ -74,7 +89,7 @@ func CreateSignatures(layout codec.Layout, signer Signer, ic, max uint32) (index
7489 return "" , nil , err
7590 }
7691
77- // Build and sign the index file
92+ // Build and sign the index file (JS-style: message = index JSON string)
7893 idx := BuildIndex (layoutIDs , layoutSigB64 )
7994 indexB64 , creatorSigB64 , err := SignIndexB64 (idx , signer )
8095 if err != nil {
@@ -90,9 +105,27 @@ func CreateSignatures(layout codec.Layout, signer Signer, ic, max uint32) (index
90105 return indexSignatureFormat , indexIDs , nil
91106}
92107
108+ // CreateSignaturesWithKeyring signs layout and index using a Cosmos keyring (legacy path).
109+ // Message signed = raw bytes passed by SignLayoutB64 / SignIndexB64:
110+ // - layout: layoutB64 string
111+ // - index: index JSON string
112+ //
113+ // The verification pipeline already handles both raw and ADR-36, so this remains valid.
114+ func CreateSignaturesWithKeyring (
115+ layout codec.Layout ,
116+ kr sdkkeyring.Keyring ,
117+ keyName string ,
118+ ic , max uint32 ,
119+ ) (string , []string , error ) {
120+ signer := func (msg []byte ) ([]byte , error ) {
121+ return keyringpkg .SignBytes (kr , keyName , msg )
122+ }
123+ return CreateSignatures (layout , signer , ic , max )
124+ }
125+
93126// adr36SignerForKeyring creates a signer that signs ADR-36 doc bytes
94127// for the given signer address. The "msg" we pass in is the *message*
95- // (layoutB64, indexJSON , etc.), and this helper wraps it into ADR-36.
128+ // (layoutB64, index JSON , etc.), and this helper wraps it into ADR-36.
96129func adr36SignerForKeyring (
97130 kr sdkkeyring.Keyring ,
98131 keyName string ,
@@ -113,6 +146,13 @@ func adr36SignerForKeyring(
113146 }
114147}
115148
149+ // CreateSignaturesWithKeyringADR36 creates signatures in the SAME way as the JS SDK:
150+ //
151+ // - layout: Keplr-like ADR-36 signature over layoutB64 string
152+ // - index: Keplr-like ADR-36 signature over index JSON string
153+ //
154+ // The resulting indexSignatureFormat string will match what JS produces for the same
155+ // layout, signer, ic, and max.
116156func CreateSignaturesWithKeyringADR36 (
117157 layout codec.Layout ,
118158 kr sdkkeyring.Keyring ,
@@ -129,3 +169,36 @@ func CreateSignaturesWithKeyringADR36(
129169
130170 return CreateSignatures (layout , signer , ic , max )
131171}
172+
173+ // SignADR36String signs a message string using the ADR-36 scheme that Keplr uses.
174+ // "message" must be the same string you'd pass to Keplr's signArbitrary, e.g.:
175+ // - layoutB64
176+ // - index JSON
177+ // - dataHash (base64 blake3)
178+ func SignADR36String (
179+ kr sdkkeyring.Keyring ,
180+ keyName string ,
181+ signerAddr string ,
182+ message string ,
183+ ) (string , error ) {
184+ // 1) message -> []byte
185+ msgBytes := []byte (message )
186+
187+ // 2) base64(UTF-8(message))
188+ dataB64 := base64 .StdEncoding .EncodeToString (msgBytes )
189+
190+ // 3) Build ADR-36 sign bytes (Keplr-accurate)
191+ docBytes , err := actionkeeper .MakeADR36AminoSignBytes (signerAddr , dataB64 )
192+ if err != nil {
193+ return "" , fmt .Errorf ("build adr36 sign bytes: %w" , err )
194+ }
195+
196+ // 4) Sign with Cosmos keyring
197+ sig , err := keyringpkg .SignBytes (kr , keyName , docBytes )
198+ if err != nil {
199+ return "" , fmt .Errorf ("sign adr36 doc: %w" , err )
200+ }
201+
202+ // 5) Wire format: base64(rsSignature)
203+ return base64 .StdEncoding .EncodeToString (sig ), nil
204+ }
0 commit comments