header_test.gno
30.95 Kb · 952 lines
1package tendermint_test
2
3import (
4 "bytes"
5 "crypto/ed25519"
6 "crypto/sha256"
7 "encoding/base64"
8 "encoding/hex"
9 "strings"
10 "testing"
11 "time"
12
13 "gno.land/p/aib/ibc/lightclient"
14 "gno.land/p/aib/ibc/lightclient/tendermint"
15 "gno.land/p/aib/ibc/types"
16 "gno.land/p/nt/uassert/v0"
17)
18
19func TestMsgHeaderValidateBasic(t *testing.T) {
20 var msg *tendermint.MsgHeader
21 testCases := []struct {
22 name string
23 malleate func()
24 expectedError string
25 }{
26 {
27 name: "valid",
28 malleate: func() {},
29 expectedError: "",
30 },
31 {
32 name: "header is nil",
33 malleate: func() { msg.Header = nil },
34 expectedError: "missing header",
35 },
36 {
37 name: "header version block invalid",
38 malleate: func() { msg.Header.Version.Block-- },
39 expectedError: "block protocol is incorrect",
40 },
41 {
42 name: "header chainId too long",
43 malleate: func() {
44 msg.Header.ChainID = fiftyOneCharChainID
45 },
46 expectedError: "chainID is too long",
47 },
48 {
49 name: "header height is zero",
50 malleate: func() { msg.Header.Height = 0 },
51 expectedError: "zero Height",
52 },
53 {
54 name: "header lastBlockID hash invalid",
55 malleate: func() {
56 msg.Header.LastBlockID.Hash = invalidHash
57 },
58 expectedError: "wrong blockID Hash",
59 },
60 {
61 name: "header lastBlockID ParSetHeader hash invalid",
62 malleate: func() {
63 msg.Header.LastBlockID.PartSetHeader.Hash = invalidHash
64 },
65 expectedError: "wrong parSetHeader Hash",
66 },
67 {
68 name: "header lastCommitHash invalid",
69 malleate: func() {
70 msg.Header.LastCommitHash = invalidHash
71 },
72 expectedError: "wrong LastCommitHash",
73 },
74 {
75 name: "header dataHash invalid",
76 malleate: func() {
77 msg.Header.DataHash = invalidHash
78 },
79 expectedError: "wrong DataHash",
80 },
81 {
82 name: "header evidenceHash invalid",
83 malleate: func() {
84 msg.Header.EvidenceHash = invalidHash
85 },
86 expectedError: "wrong EvidenceHash",
87 },
88 {
89 name: "header proposerAddress invalid",
90 malleate: func() {
91 msg.Header.ProposerAddress = []byte("adr_too_short")
92 },
93 expectedError: "invalid ProposerAddress",
94 },
95 {
96 name: "header validatorHash invalid",
97 malleate: func() {
98 msg.Header.ValidatorsHash = invalidHash
99 },
100 expectedError: "wrong ValidatorsHash",
101 },
102 {
103 name: "header nextValidatorHash invalid",
104 malleate: func() {
105 msg.Header.NextValidatorsHash = invalidHash
106 },
107 expectedError: "wrong NextValidatorsHash",
108 },
109 {
110 name: "header consensusHash invalid",
111 malleate: func() {
112 msg.Header.ConsensusHash = invalidHash
113 },
114 expectedError: "wrong ConsensusHash",
115 },
116 {
117 name: "header lastResultHash invalid",
118 malleate: func() {
119 msg.Header.LastResultsHash = invalidHash
120 },
121 expectedError: "wrong LastResultsHash",
122 },
123 {
124 name: "commit is nil",
125 malleate: func() { msg.Commit = nil },
126 expectedError: "missing commit",
127 },
128 {
129 name: "commit round negative",
130 malleate: func() { msg.Commit.Round = -1 },
131 expectedError: "negative Round",
132 },
133 {
134 name: "commit blockId is zero",
135 malleate: func() {
136 msg.Commit.BlockID.Hash = nil
137 msg.Commit.BlockID.PartSetHeader.Total = 0
138 msg.Commit.BlockID.PartSetHeader.Hash = nil
139 },
140 expectedError: "commit cannot be for nil block",
141 },
142 {
143 name: "commit signatures are empty",
144 malleate: func() {
145 msg.Commit.Signatures = nil
146 },
147 expectedError: "no signatures in commit",
148 },
149 {
150 name: "commit signature blockIdFlag is invalid",
151 malleate: func() {
152 msg.Commit.Signatures[0].BlockIDFlag = tendermint.BlockIDFlagUnknown
153 },
154 expectedError: "unknown BlockIDFlag",
155 },
156 {
157 name: "commit signature blockIdAbsent validatorAddress is not empty",
158 malleate: func() {
159 msg.Commit.Signatures[0].BlockIDFlag = tendermint.BlockIDFlagAbsent
160 },
161 expectedError: "validator address is present",
162 },
163 {
164 name: "commit signature blockIdAbsent timestamp is not empty",
165 malleate: func() {
166 msg.Commit.Signatures[0].BlockIDFlag = tendermint.BlockIDFlagAbsent
167 msg.Commit.Signatures[0].ValidatorAddress = nil
168 },
169 expectedError: "time is present",
170 },
171 {
172 name: "commit signature blockIdAbsent signature is not empty",
173 malleate: func() {
174 msg.Commit.Signatures[0].BlockIDFlag = tendermint.BlockIDFlagAbsent
175 msg.Commit.Signatures[0].ValidatorAddress = nil
176 msg.Commit.Signatures[0].Timestamp = time.Time{}
177 },
178 expectedError: "signature is present",
179 },
180 {
181 name: "commit signature validatorAddress is invalid",
182 malleate: func() {
183 msg.Commit.Signatures[0].ValidatorAddress = []byte("addr_too_short")
184 },
185 expectedError: "expected ValidatorAddress size to be",
186 },
187 {
188 name: "commit signature is missing",
189 malleate: func() {
190 msg.Commit.Signatures[0].Signature = nil
191 },
192 expectedError: "signature is missing",
193 },
194 {
195 name: "commit signature is invalid",
196 malleate: func() {
197 msg.Commit.Signatures[0].Signature = bytes.Repeat([]byte{1}, tendermint.MaxSignatureSize+1)
198 },
199 expectedError: "signature is too big",
200 },
201 {
202 name: "commit and header height mismatch",
203 malleate: func() { msg.Commit.Height-- },
204 expectedError: "header and commit height mismatch: 3 vs 2",
205 },
206 {
207 name: "trusted height is equal to header height",
208 malleate: func() {
209 msg.TrustedHeight = msg.GetHeight()
210 },
211 expectedError: "trustedHeight 0/3 must be less than header height 0/3",
212 },
213 {
214 name: "trusted height is lower to header height",
215 malleate: func() {
216 msg.TrustedHeight = msg.GetHeight()
217 msg.TrustedHeight.RevisionHeight++
218 },
219 expectedError: "trustedHeight 0/4 must be less than header height 0/3",
220 },
221 {
222 name: "validator set nil",
223 malleate: func() { msg.ValidatorSet = nil },
224 expectedError: "validator set is nil",
225 },
226 {
227 name: "trusted validator set nil",
228 malleate: func() { msg.TrustedValidators = nil },
229 expectedError: "trusted validator set is nil",
230 },
231 {
232 name: "validator set hash mismatch",
233 malleate: func() {
234 msg.ValidatorSet.Validators[0].PubKey = []byte{}
235 },
236 expectedError: "validator set does not match hash",
237 },
238 {
239 name: "commit signatures and validators count are different",
240 malleate: func() {
241 msg.Commit.Signatures = msg.Commit.Signatures[:1]
242 },
243 expectedError: "Invalid commit -- wrong set size: 2 vs 1",
244 },
245 {
246 name: "commit.blockid.hash != header.hash()",
247 malleate: func() {
248 msg.Commit.BlockID.Hash = hash("")
249 },
250 expectedError: `expected BlockID#Hash and Header#Hash to be the same, got "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" != "4f633a8766a596303f9139cd4706eb03484be5f5521b724c57b7021fb6889e1d"`,
251 },
252 }
253
254 if lightclient.Tendermint != msg.ClientType() {
255 t.Fatal("client type is not tendermint")
256 }
257
258 for _, tc := range testCases {
259 t.Run(tc.name, func(t *testing.T) {
260 var (
261 newHeight = uint64(3)
262 newTimestamp = time.Now()
263 val1 = &tendermint.Validator{
264 Address: genAddr("val1"),
265 PubKey: genPubkey("val1_pubkey"),
266 VotingPower: 2,
267 }
268 val2 = &tendermint.Validator{
269 Address: genAddr("val2"),
270 PubKey: genPubkey("val2_pubkey"),
271 VotingPower: 3,
272 }
273 valset = tendermint.NewValset(val1, val2)
274 trustedval1 = &tendermint.Validator{
275 Address: genAddr("val1"),
276 PubKey: genPubkey("val1_pubkey"),
277 VotingPower: 2,
278 }
279 trustedval2 = &tendermint.Validator{
280 Address: genAddr("val2"),
281 PubKey: genPubkey("val2_pubkey"),
282 VotingPower: 3,
283 }
284 nextValset = tendermint.NewValset(trustedval1, trustedval2)
285 trustedValset = tendermint.NewValset(trustedval1, trustedval2)
286 trustedHeight = types.NewHeight(0, 1)
287 apphash = hash("apphash")
288 signatures = []tendermint.CommitSig{
289 {
290 BlockIDFlag: tendermint.BlockIDFlagCommit,
291 ValidatorAddress: valset.Validators[0].Address,
292 Timestamp: toTime("2025-09-25T07:55:57.306746166Z"),
293 Signature: b64Dec("qtv1z4S2Q6T87vGQo0lrjRZqv9PrHIji4pTyviMnVyGx9td6eySdzwQwCthwmihU48ebNlFiMlFJ0CT891UmDg=="),
294 },
295 {
296 BlockIDFlag: tendermint.BlockIDFlagCommit,
297 ValidatorAddress: valset.Validators[1].Address,
298 Timestamp: toTime("2025-09-25T07:55:57.310583641Z"),
299 Signature: b64Dec("Q5E6Kjma00n/T98rC9qJmoB6JTGFX/IB+mDVs4Wd1h0eJ8fabY/6oI8zdoU6/7W6VR6wjpHyWBsJrpGT6C0LCg=="),
300 },
301 }
302 )
303 msg = newMsgHeader(
304 chainID, newTimestamp, apphash, newHeight, trustedHeight, valset,
305 nextValset, trustedValset, signatures,
306 )
307 tc.malleate()
308
309 err := msg.ValidateBasic()
310
311 if tc.expectedError == "" && err != nil {
312 t.Errorf("expected no error got %v", err)
313 return
314 }
315 if tc.expectedError != "" {
316 if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
317 t.Errorf("expected error %s, got %s", tc.expectedError, err)
318 }
319 }
320 })
321 }
322}
323
324func TestHeaderHash(t *testing.T) {
325 tests := []struct {
326 desc string
327 header *tendermint.Header
328 expectedHash string // hex encoded
329 }{
330 {
331 desc: "generates expected hash",
332 header: &tendermint.Header{
333 Version: tendermint.Consensus{Block: 1, App: 2},
334 ChainID: "chainId",
335 Height: 3,
336 Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC),
337 LastBlockID: tendermint.BlockID{
338 Hash: make([]byte, sha256.Size),
339 PartSetHeader: tendermint.PartSetHeader{
340 Total: 6,
341 Hash: make([]byte, sha256.Size),
342 },
343 },
344 LastCommitHash: hash("last_commit_hash"),
345 DataHash: hash("data_hash"),
346 ValidatorsHash: hash("validators_hash"),
347 NextValidatorsHash: hash("next_validators_hash"),
348 ConsensusHash: hash("consensus_hash"),
349 AppHash: hash("app_hash"),
350 LastResultsHash: hash("last_results_hash"),
351 EvidenceHash: hash("evidence_hash"),
352 ProposerAddress: genAddr("proposer_address"),
353 },
354 expectedHash: "f740121f553b5418c3efbd343c2dbfe9e007bb67b0d020a0741374bab65242a4",
355 },
356 {
357 desc: "nil header yields nil",
358 header: nil,
359 expectedHash: "",
360 },
361 {
362 desc: "nil ValidatorsHash yields nil",
363 header: &tendermint.Header{
364 Version: tendermint.Consensus{Block: 1, App: 2},
365 ChainID: "chainId",
366 Height: 3,
367 Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC),
368 LastBlockID: tendermint.BlockID{
369 Hash: make([]byte, sha256.Size),
370 PartSetHeader: tendermint.PartSetHeader{
371 Total: 6,
372 Hash: make([]byte, sha256.Size),
373 },
374 },
375 LastCommitHash: hash("last_commit_hash"),
376 DataHash: hash("data_hash"),
377 ValidatorsHash: nil,
378 NextValidatorsHash: hash("next_validators_hash"),
379 ConsensusHash: hash("consensus_hash"),
380 AppHash: hash("app_hash"),
381 LastResultsHash: hash("last_results_hash"),
382 EvidenceHash: hash("evidence_hash"),
383 ProposerAddress: genAddr("proposer_address"),
384 },
385 expectedHash: "",
386 },
387 }
388 for _, tt := range tests {
389 t.Run(tt.desc, func(t *testing.T) {
390 h := tt.header.Hash()
391
392 uassert.Equal(t, tt.expectedHash, hex.EncodeToString(h))
393 })
394 }
395}
396
397func TestCommitBytesToSign(t *testing.T) {
398 t.Run("update client 1", func(t *testing.T) {
399 var (
400 // ref: https://www.mintscan.io/atomone/tx/E7A075A6B6BC56ED2006A91F7833354C7F721CC9618BF09F276B4F58B119149B?sector=json
401 // (truncated with only the first 2 validators)
402 // {
403 // "@type": "/ibc.core.client.v1.MsgUpdateClient",
404 // "client_id": "07-tendermint-2",
405 // "client_message": {
406 // "@type": "/ibc.lightclients.tendermint.v1.Header",
407 // "signed_header": {
408 // "header": {
409 // "version": {
410 // "block": "11",
411 // "app": "0"
412 // },
413 // "chain_id": "osmosis-1",
414 // "height": "44873552",
415 // "time": "2025-09-25T07:55:56.299553312Z",
416 // "last_block_id": {
417 // "hash": "rV4iVJhw1gwx1d7nij1tiI0211CsYbqzrlT1bAKB6FM=",
418 // "part_set_header": {
419 // "total": 1,
420 // "hash": "TmTAPEknTC5O3/XV12onhZvcvLiK0X6CdPFND4NMARw="
421 // }
422 // },
423 // "last_commit_hash": "I18uOE4JQq7ZpkGb5oZQqYxLSWO86nFZ8yEICfs+XIU=",
424 // "data_hash": "Wv50wpNbp0d8P7bnNPiDqKOO8fo2RCvs8H8LI2c5dbo=",
425 // "validators_hash": "ehQCurs4+UgURefaP7Sw1T/MnviV6jCE8SLFMGB2Mis=",
426 // "next_validators_hash": "ehQCurs4+UgURefaP7Sw1T/MnviV6jCE8SLFMGB2Mis=",
427 // "consensus_hash": "JIS7qLmeUx4aP2QiNi59PuuZqDJRGZaKNV8FwJ7t1As=",
428 // "app_hash": "DAHBO3lYaIFu9XmplThmF7ijXG7YwDZawGSDKKCHh7c=",
429 // "last_results_hash": "aSOQCy7TAjnW/YXTu8OJjzKmnrLA3loaf0i0y+D9U/Q=",
430 // "evidence_hash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
431 // "proposer_address": "ZraWZuv3dufry+GXq6RmpxLicHY="
432 // },
433 // "commit": {
434 // "height": "44873552",
435 // "round": 0,
436 // "block_id": {
437 // "hash": "NpiImIJoaSaIucwNs5cqpgMsL/8wxEPYC3P0jA5aQSI=",
438 // "part_set_header": {
439 // "total": 1,
440 // "hash": "QqzwnLzvixIcUz+hPeUQjDV6NaLkFRKXACCxJIrBHzw="
441 // }
442 // },
443 // "signatures": [
444 // {
445 // "block_id_flag": "BLOCK_ID_FLAG_COMMIT",
446 // "validator_address": "y1pjuR6PTujbk1lCy+JXJGNkeeA=",
447 // "timestamp": "2025-09-25T07:55:57.306746166Z",
448 // "signature": "qtv1z4S2Q6T87vGQo0lrjRZqv9PrHIji4pTyviMnVyGx9td6eySdzwQwCthwmihU48ebNlFiMlFJ0CT891UmDg=="
449 // },
450 // {
451 // "block_id_flag": "BLOCK_ID_FLAG_COMMIT",
452 // "validator_address": "ZraWZuv3dufry+GXq6RmpxLicHY=",
453 // "timestamp": "2025-09-25T07:55:57.310583641Z",
454 // "signature": "Q5E6Kjma00n/T98rC9qJmoB6JTGFX/IB+mDVs4Wd1h0eJ8fabY/6oI8zdoU6/7W6VR6wjpHyWBsJrpGT6C0LCg=="
455 // }
456 // ]
457 // }
458 // },
459 // "validator_set": {
460 // "validators": [
461 // {
462 // "address": "y1pjuR6PTujbk1lCy+JXJGNkeeA=",
463 // "pub_key": {
464 // "ed25519": "6Nz09YGHzwWxjczG0IhK4Iv0qY2IcX0P/5KitvRXTUc="
465 // },
466 // "voting_power": "19087034",
467 // "proposer_priority": "0"
468 // },
469 // {
470 // "address": "ZraWZuv3dufry+GXq6RmpxLicHY=",
471 // "pub_key": {
472 // "ed25519": "wB25StLxbzmD0uTiFiH6xySZd0H13kyanNUvvlUpa34="
473 // },
474 // "voting_power": "11979324",
475 // "proposer_priority": "0"
476 // }
477 // ],
478 // "proposer": {
479 // "address": "ZraWZuv3dufry+GXq6RmpxLicHY=",
480 // "pub_key": {
481 // "ed25519": "wB25StLxbzmD0uTiFiH6xySZd0H13kyanNUvvlUpa34="
482 // },
483 // "voting_power": "11979324",
484 // "proposer_priority": "0"
485 // },
486 // "total_voting_power": "276866533"
487 // },
488 // "trusted_height": {
489 // "revision_number": "1",
490 // "revision_height": "44873531"
491 // },
492 // "trusted_validators": {
493 // "validators": [
494 // {
495 // "address": "y1pjuR6PTujbk1lCy+JXJGNkeeA=",
496 // "pub_key": {
497 // "ed25519": "6Nz09YGHzwWxjczG0IhK4Iv0qY2IcX0P/5KitvRXTUc="
498 // },
499 // "voting_power": "19087034",
500 // "proposer_priority": "0"
501 // },
502 // {
503 // "address": "ZraWZuv3dufry+GXq6RmpxLicHY=",
504 // "pub_key": {
505 // "ed25519": "wB25StLxbzmD0uTiFiH6xySZd0H13kyanNUvvlUpa34="
506 // },
507 // "voting_power": "11979324",
508 // "proposer_priority": "0"
509 // }
510 // ],
511 // "proposer": {
512 // "address": "xx7zwESZhHO0zsyumXcZdvGY1KU=",
513 // "pub_key": {
514 // "ed25519": "c28EquE3UMuallczX/Bu1UHpV9dM/yTaIVIR+ftY1g4="
515 // },
516 // "voting_power": "9284967",
517 // "proposer_priority": "0"
518 // },
519 // "total_voting_power": "276866533"
520 // }
521 // },
522 // "signer": "atone14d0ka5jcuhf9v3n7qpdpmc9qhgsd6uae2g7jxy"
523 //}
524 chainID = "osmosis-1"
525 timestamp = toTime("2025-09-25T07:55:56.299553312Z")
526 apphash = b64Dec("DAHBO3lYaIFu9XmplThmF7ijXG7YwDZawGSDKKCHh7c=")
527 height = uint64(44873552)
528 blockhash = b64Dec("NpiImIJoaSaIucwNs5cqpgMsL/8wxEPYC3P0jA5aQSI=")
529 parsethash = b64Dec("QqzwnLzvixIcUz+hPeUQjDV6NaLkFRKXACCxJIrBHzw=")
530 consensushash = b64Dec("JIS7qLmeUx4aP2QiNi59PuuZqDJRGZaKNV8FwJ7t1As=")
531 val1 = tendermint.NewValidator(
532 "y1pjuR6PTujbk1lCy+JXJGNkeeA=", "6Nz09YGHzwWxjczG0IhK4Iv0qY2IcX0P/5KitvRXTUc=", 2,
533 )
534 val2 = tendermint.NewValidator(
535 "ZraWZuv3dufry+GXq6RmpxLicHY=", "wB25StLxbzmD0uTiFiH6xySZd0H13kyanNUvvlUpa34=", 3,
536 )
537 valset = tendermint.NewValset(val1, val2)
538 nextValset = tendermint.NewValset(val1, val2)
539 signatures = []tendermint.CommitSig{
540 {
541 BlockIDFlag: tendermint.BlockIDFlagCommit,
542 ValidatorAddress: valset.Validators[0].Address,
543 Timestamp: toTime("2025-09-25T07:55:57.306746166Z"),
544 Signature: b64Dec("qtv1z4S2Q6T87vGQo0lrjRZqv9PrHIji4pTyviMnVyGx9td6eySdzwQwCthwmihU48ebNlFiMlFJ0CT891UmDg=="),
545 },
546 {
547 BlockIDFlag: tendermint.BlockIDFlagCommit,
548 ValidatorAddress: valset.Validators[1].Address,
549 Timestamp: toTime("2025-09-25T07:55:57.310583641Z"),
550 Signature: b64Dec("Q5E6Kjma00n/T98rC9qJmoB6JTGFX/IB+mDVs4Wd1h0eJ8fabY/6oI8zdoU6/7W6VR6wjpHyWBsJrpGT6C0LCg=="),
551 },
552 }
553 header = &tendermint.Header{
554 Version: tendermint.Consensus{
555 Block: tendermint.BlockProtocol,
556 App: 0,
557 },
558 ChainID: chainID,
559 Height: height,
560 Time: timestamp,
561 LastBlockID: tendermint.BlockID{
562 Hash: blockhash,
563 PartSetHeader: tendermint.PartSetHeader{
564 Total: 1,
565 Hash: parsethash,
566 },
567 },
568 LastCommitHash: hash("last_commit_hash"),
569 DataHash: hash("data_hash"),
570 ValidatorsHash: valset.Hash(),
571 NextValidatorsHash: nextValset.Hash(),
572 ConsensusHash: consensushash,
573 AppHash: apphash,
574 LastResultsHash: hash("last_result_hash"),
575 EvidenceHash: hash("evidence_hash"),
576 ProposerAddress: valset.Proposer.Address,
577 }
578
579 msg = &tendermint.MsgHeader{
580 Header: header,
581 Commit: &tendermint.Commit{
582 Height: height,
583 Round: 0,
584 BlockID: tendermint.BlockID{
585 Hash: blockhash,
586 PartSetHeader: tendermint.PartSetHeader{
587 Total: 1,
588 Hash: parsethash,
589 },
590 },
591 Signatures: signatures,
592 },
593 ValidatorSet: valset,
594 TrustedHeight: types.Height{},
595 TrustedValidators: valset,
596 }
597
598 // expectedBytesToSign are computed from cometbft/types.Commit.VoteSignBytes() function
599 expectedBytesToSign = []string{
600 "6e08021150b7ac020000000022480a20369888988268692688b9cc0db3972aa6032c2fff30c443d80b73f48c0e5a412212240801122042acf09cbcef8b121c533fa13de5108c357a35a2e41512970020b1248ac11f3c2a0c088debd3c60610b6a6a2920132096f736d6f7369732d31",
601 "6e08021150b7ac020000000022480a20369888988268692688b9cc0db3972aa6032c2fff30c443d80b73f48c0e5a412212240801122042acf09cbcef8b121c533fa13de5108c357a35a2e41512970020b1248ac11f3c2a0c088debd3c60610d9c28c940132096f736d6f7369732d31",
602 }
603 )
604
605 testVotesBytesToSign(t, msg, expectedBytesToSign)
606 })
607
608 t.Run("update client 2", func(t *testing.T) {
609 var (
610 // Ref: https://www.mintscan.io/atomone/tx/DAE6D01666864DBB1F7AFDDD2C20B0C22D34974E9682B15A5049C24574EBEC70?sector=json
611 // (truncated with the 3 first validators)
612 //{
613 // "@type": "/ibc.core.client.v1.MsgUpdateClient",
614 // "client_id": "07-tendermint-1",
615 // "client_message": {
616 // "@type": "/ibc.lightclients.tendermint.v1.Header",
617 // "signed_header": {
618 // "header": {
619 // "version": {
620 // "block": "11",
621 // "app": "0"
622 // },
623 // "chain_id": "beezee-1",
624 // "height": "19282029",
625 // "time": "2025-09-25T14:00:43.469479971Z",
626 // "last_block_id": {
627 // "hash": "cx4wWNgIMOrgnXOPFlXMTV6je4GbYZjRhgcJ7RNs0g4=",
628 // "part_set_header": {
629 // "total": 1,
630 // "hash": "Nv1TMWmm4ze2Y1p3pBLF9VOPJaPOARp4ypZglD4HZmo="
631 // }
632 // },
633 // "last_commit_hash": "zNO6+/eHsl8hih5kYSEDQql/vZUqTyuc6qJD6AnjrcI=",
634 // "data_hash": "1EbR1y5AQgdELcRJTPf7ql6BJI5J9Ji9mTkdNXElcj4=",
635 // "validators_hash": "ovLukv8mGP7nzYUiYBB8gUWQMvPZehcxQi86HsRGRW4=",
636 // "next_validators_hash": "ovLukv8mGP7nzYUiYBB8gUWQMvPZehcxQi86HsRGRW4=",
637 // "consensus_hash": "BOEs4QnQgrOaBfgH2qUBI3M1r0rPLx++gYSRGuYVAns=",
638 // "app_hash": "XI9E+rmDbjwfn/EvQVp7CAqygaEI8Fingdh3xR+7aKw=",
639 // "last_results_hash": "pgDffcPvKUJK3G2+adQmiLdiY5Rh7jOtRx81ytLcq4k=",
640 // "evidence_hash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
641 // "proposer_address": "ysbGRHeOh4hD9fUbkM1RHP9bVfc="
642 // },
643 // "commit": {
644 // "height": "19282029",
645 // "round": 0,
646 // "block_id": {
647 // "hash": "q82PBaLn4Vg3oLm0v670BOm1Ay+CR69DyBqNA/V//l4=",
648 // "part_set_header": {
649 // "total": 1,
650 // "hash": "PqBqLFCb6YhCQZFDnOeHzp1kHAbKs7fwHqOwnn8ha2s="
651 // }
652 // },
653 // "signatures": [
654 // {
655 // "block_id_flag": "BLOCK_ID_FLAG_ABSENT",
656 // "validator_address": null,
657 // "timestamp": "0001-01-01T00:00:00Z",
658 // "signature": null
659 // },
660 // {
661 // "block_id_flag": "BLOCK_ID_FLAG_COMMIT",
662 // "validator_address": "mYLjIF9AxwR+BBlkD9M3f+xqKNE=",
663 // "timestamp": "2025-09-25T14:00:49.373544902Z",
664 // "signature": "rgUFLD3Yk9JdcfWSqcscXeUZVY+Q7E1jQ1Eumkh/LT2fUI4dsk2NBeOhjyKaF5Ey/q6DwoINss6iSxNPj8s2Ag=="
665 // },
666 // {
667 // "block_id_flag": "BLOCK_ID_FLAG_COMMIT",
668 // "validator_address": "ysbGRHeOh4hD9fUbkM1RHP9bVfc=",
669 // "timestamp": "2025-09-25T14:00:49.396076476Z",
670 // "signature": "thHpLNT6555JHoNr10iBpp8NJemRMPKMwnV4QDL6gLpaYChqrQND+8K4hQrC8Ld2IAuIT92ZHFWOmoVH9bIcBw=="
671 // }
672 // ]
673 // }
674 // },
675 // "validator_set": {
676 // "validators": [
677 // {
678 // "address": "h9mY0OvyfYUX7rMfkXO+9bpEWdo=",
679 // "pub_key": {
680 // "ed25519": "wF3Daj7tqX8nVOJd2WCkMVvqyx/lPzuI5/y3wwSRwbY="
681 // },
682 // "voting_power": "7870904",
683 // "proposer_priority": "0"
684 // },
685 // {
686 // "address": "mYLjIF9AxwR+BBlkD9M3f+xqKNE=",
687 // "pub_key": {
688 // "ed25519": "dzCYrLu2sjpWSiEd2MVqsr+Q6ocBDUUUrRKBehGOeLM="
689 // },
690 // "voting_power": "7847038",
691 // "proposer_priority": "0"
692 // },
693 // {
694 // "address": "ysbGRHeOh4hD9fUbkM1RHP9bVfc=",
695 // "pub_key": {
696 // "ed25519": "tYyou2DP3JWDrvKPtbAZpTCEtzqr9h1nsi/srNZwLiY="
697 // },
698 // "voting_power": "7381171",
699 // "proposer_priority": "0"
700 // }
701 // ],
702 // "proposer": {
703 // "address": "ysbGRHeOh4hD9fUbkM1RHP9bVfc=",
704 // "pub_key": {
705 // "ed25519": "tYyou2DP3JWDrvKPtbAZpTCEtzqr9h1nsi/srNZwLiY="
706 // },
707 // "voting_power": "7381171",
708 // "proposer_priority": "0"
709 // },
710 // "total_voting_power": "169513553"
711 // },
712 // "trusted_height": {
713 // "revision_number": "1",
714 // "revision_height": "19280755"
715 // },
716 // "trusted_validators": {
717 // "validators": [
718 // {
719 // "address": "h9mY0OvyfYUX7rMfkXO+9bpEWdo=",
720 // "pub_key": {
721 // "ed25519": "wF3Daj7tqX8nVOJd2WCkMVvqyx/lPzuI5/y3wwSRwbY="
722 // },
723 // "voting_power": "7870904",
724 // "proposer_priority": "0"
725 // },
726 // {
727 // "address": "mYLjIF9AxwR+BBlkD9M3f+xqKNE=",
728 // "pub_key": {
729 // "ed25519": "dzCYrLu2sjpWSiEd2MVqsr+Q6ocBDUUUrRKBehGOeLM="
730 // },
731 // "voting_power": "7847038",
732 // "proposer_priority": "0"
733 // },
734 // {
735 // "address": "ysbGRHeOh4hD9fUbkM1RHP9bVfc=",
736 // "pub_key": {
737 // "ed25519": "tYyou2DP3JWDrvKPtbAZpTCEtzqr9h1nsi/srNZwLiY="
738 // },
739 // "voting_power": "7381171",
740 // "proposer_priority": "0"
741 // }
742 // ],
743 // "proposer": {
744 // "address": "IhfskqTnO6eehukPpsQYBCnrfEE=",
745 // "pub_key": {
746 // "ed25519": "4Payp8jGB+/HF2opX32Ox+rwJ4O/WlCRz4sKwyuQfMA="
747 // },
748 // "voting_power": "6849070",
749 // "proposer_priority": "0"
750 // },
751 // "total_voting_power": "169513421"
752 // }
753 // },
754 // "signer": "atone1z7p3628vmw8psjwwzlcak0xqvgyssmwpa3guqm"
755 //}
756 chainID = "beezee-1"
757 timestamp = toTime("2025-09-25T14:00:43.469479971Z")
758 apphash = b64Dec("XI9E+rmDbjwfn/EvQVp7CAqygaEI8Fingdh3xR+7aKw=")
759 height = uint64(19282029)
760 blockhash = b64Dec("q82PBaLn4Vg3oLm0v670BOm1Ay+CR69DyBqNA/V//l4=")
761 parsethash = b64Dec("PqBqLFCb6YhCQZFDnOeHzp1kHAbKs7fwHqOwnn8ha2s=")
762 consensushash = b64Dec("BOEs4QnQgrOaBfgH2qUBI3M1r0rPLx++gYSRGuYVAns=")
763 val1 = tendermint.NewValidator(
764 "h9mY0OvyfYUX7rMfkXO+9bpEWdo=", "wF3Daj7tqX8nVOJd2WCkMVvqyx/lPzuI5/y3wwSRwbY=", 2,
765 )
766 val2 = tendermint.NewValidator(
767 "mYLjIF9AxwR+BBlkD9M3f+xqKNE=", "dzCYrLu2sjpWSiEd2MVqsr+Q6ocBDUUUrRKBehGOeLM=", 3,
768 )
769 val3 = tendermint.NewValidator(
770 "ysbGRHeOh4hD9fUbkM1RHP9bVfc=", "tYyou2DP3JWDrvKPtbAZpTCEtzqr9h1nsi/srNZwLiY=", 4,
771 )
772 valset = tendermint.NewValset(val1, val2, val3)
773 nextValset = tendermint.NewValset(val1, val2, val3)
774 signatures = []tendermint.CommitSig{
775 {
776 BlockIDFlag: tendermint.BlockIDFlagAbsent,
777 ValidatorAddress: nil,
778 Timestamp: time.Time{},
779 Signature: nil,
780 },
781 {
782 BlockIDFlag: tendermint.BlockIDFlagCommit,
783 ValidatorAddress: valset.Validators[1].Address,
784 Timestamp: toTime("2025-09-25T14:00:49.373544902Z"),
785 Signature: b64Dec("rgUFLD3Yk9JdcfWSqcscXeUZVY+Q7E1jQ1Eumkh/LT2fUI4dsk2NBeOhjyKaF5Ey/q6DwoINss6iSxNPj8s2Ag=="),
786 },
787 {
788 BlockIDFlag: tendermint.BlockIDFlagCommit,
789 ValidatorAddress: valset.Validators[2].Address,
790 Timestamp: toTime("2025-09-25T14:00:49.396076476Z"),
791 Signature: b64Dec("thHpLNT6555JHoNr10iBpp8NJemRMPKMwnV4QDL6gLpaYChqrQND+8K4hQrC8Ld2IAuIT92ZHFWOmoVH9bIcBw=="),
792 },
793 }
794 header = &tendermint.Header{
795 Version: tendermint.Consensus{
796 Block: tendermint.BlockProtocol,
797 App: 0,
798 },
799 ChainID: chainID,
800 Height: height,
801 Time: timestamp,
802 LastBlockID: tendermint.BlockID{
803 Hash: blockhash,
804 PartSetHeader: tendermint.PartSetHeader{
805 Total: 1,
806 Hash: parsethash,
807 },
808 },
809 LastCommitHash: hash("last_commit_hash"),
810 DataHash: hash("data_hash"),
811 ValidatorsHash: valset.Hash(),
812 NextValidatorsHash: nextValset.Hash(),
813 ConsensusHash: consensushash,
814 AppHash: apphash,
815 LastResultsHash: hash("last_result_hash"),
816 EvidenceHash: hash("evidence_hash"),
817 ProposerAddress: valset.Proposer.Address,
818 }
819
820 msg = &tendermint.MsgHeader{
821 Header: header,
822 Commit: &tendermint.Commit{
823 Height: height,
824 Round: 0,
825 BlockID: tendermint.BlockID{
826 Hash: blockhash,
827 PartSetHeader: tendermint.PartSetHeader{
828 Total: 1,
829 Hash: parsethash,
830 },
831 },
832 Signatures: signatures,
833 },
834 ValidatorSet: valset,
835 TrustedHeight: types.Height{},
836 TrustedValidators: valset,
837 }
838
839 // expectedBytesToSign are computed from cometbft/types.Commit.VoteSignBytes() function
840 expectedBytesToSign = []string{
841 "220802116d382601000000002a0b088092b8c398feffffff0132086265657a65652d31",
842 "6d0802116d3826010000000022480a20abcd8f05a2e7e15837a0b9b4bfaef404e9b5032f8247af43c81a8d03f57ffe5e1224080112203ea06a2c509be988424191439ce787ce9d641c06cab3b7f01ea3b09e7f216b6b2a0c089196d5c60610c6af8fb20132086265657a65652d31",
843 "6d0802116d3826010000000022480a20abcd8f05a2e7e15837a0b9b4bfaef404e9b5032f8247af43c81a8d03f57ffe5e1224080112203ea06a2c509be988424191439ce787ce9d641c06cab3b7f01ea3b09e7f216b6b2a0c089196d5c60610bccbeebc0132086265657a65652d31",
844 }
845 )
846
847 testVotesBytesToSign(t, msg, expectedBytesToSign)
848 })
849}
850
851func testVotesBytesToSign(t *testing.T, msg *tendermint.MsgHeader, expectedBytesToSign []string) {
852 for idx, val := range msg.ValidatorSet.Validators {
853 bz := msg.Commit.BytesToSign(msg.Header.ChainID, idx)
854 if h := hex.EncodeToString(bz); h != expectedBytesToSign[idx] {
855 t.Errorf("wrong bytes-to-sign, got %s, want %s", h, expectedBytesToSign[idx])
856 continue
857 }
858 if msg.Commit.Signatures[idx].BlockIDFlag != tendermint.BlockIDFlagCommit {
859 // skip non signed block
860 continue
861 }
862
863 v := ed25519.Verify(val.PubKey, bz, msg.Commit.Signatures[idx].Signature)
864
865 if !v {
866 t.Errorf("failed to verify signature for validator index %d", idx)
867 }
868 }
869}
870
871func b64Dec(s string) []byte {
872 bz, err := base64.StdEncoding.DecodeString(s)
873 if err != nil {
874 panic(err)
875 }
876 return bz
877}
878
879func toTime(s string) time.Time {
880 t, err := time.Parse(time.RFC3339Nano, s)
881 if err != nil {
882 panic(err)
883 }
884 return t
885}
886
887func genAddr(seed string) []byte {
888 return hash(seed)[:tendermint.AddressSize]
889}
890
891func genSignature(seed string) []byte {
892 return hash(seed)[:tendermint.MaxSignatureSize]
893}
894
895func genPubkey(seed string) []byte {
896 return hash(seed)[:32]
897}
898
899func hash(s string) []byte {
900 h := sha256.Sum256([]byte(s))
901 return h[:]
902}
903
904func newMsgHeader(chainID string, timestamp time.Time,
905 apphash []byte, height uint64, trustedHeight types.Height,
906 valset, nextValset, trustedValset *tendermint.ValidatorSet,
907 signatures []tendermint.CommitSig) *tendermint.MsgHeader {
908 header := &tendermint.Header{
909 Version: tendermint.Consensus{
910 Block: tendermint.BlockProtocol,
911 App: 0,
912 },
913 ChainID: chainID,
914 Height: height,
915 Time: timestamp,
916 LastBlockID: tendermint.BlockID{
917 Hash: hash("last_block_hash"),
918 PartSetHeader: tendermint.PartSetHeader{
919 Total: 1,
920 Hash: hash("last_block_partset_hash"),
921 },
922 },
923 LastCommitHash: hash("last_commit_hash"),
924 DataHash: hash("data_hash"),
925 ValidatorsHash: valset.Hash(),
926 NextValidatorsHash: nextValset.Hash(),
927 ConsensusHash: hash("consensus_hash"),
928 AppHash: apphash,
929 LastResultsHash: hash("last_results_hash"),
930 EvidenceHash: hash("evidence_hash"),
931 ProposerAddress: valset.Validators[0].Address,
932 }
933
934 return &tendermint.MsgHeader{
935 Header: header,
936 Commit: &tendermint.Commit{
937 Height: height,
938 Round: 0,
939 BlockID: tendermint.BlockID{
940 Hash: header.Hash(), // compute expected block hash
941 PartSetHeader: tendermint.PartSetHeader{
942 Total: 1,
943 Hash: hash("block_partset_hash"),
944 },
945 },
946 Signatures: signatures,
947 },
948 ValidatorSet: valset,
949 TrustedHeight: trustedHeight,
950 TrustedValidators: trustedValset,
951 }
952}