package tendermint_test import ( "strings" "testing" "time" "gno.land/p/aib/ibc/lightclient/tendermint" "gno.land/p/aib/ibc/types" ) func TestMisbehaviourValidateBasic(t *testing.T) { var misbehaviour *tendermint.Misbehaviour tests := []struct { name string malleate func() expErr string }{ { name: "valid misbehaviour, two headers of different height same time", malleate: func() {}, expErr: "", }, { name: "invalid misbehaviour, header1 is nil", malleate: func() { misbehaviour.Header1 = nil }, expErr: "misbehaviour Header1 cannot be nil", }, { name: "invalid misbehaviour, header2 is nil", malleate: func() { misbehaviour.Header2 = nil }, expErr: "misbehaviour Header2 cannot be nil", }, { name: "invalid misbehaviour, header1 trusted height is 0", malleate: func() { misbehaviour.Header1.TrustedHeight = types.NewHeight(0, 0) }, expErr: "misbehaviour Header1 cannot have zero revision height", }, { name: "invalid misbehaviour, header2 tusted height is 0", malleate: func() { misbehaviour.Header2.TrustedHeight = types.NewHeight(0, 0) }, expErr: "misbehaviour Header2 cannot have zero revision height", }, { name: "invalid misbehaviour, header1 trusted val is nil", malleate: func() { misbehaviour.Header1.TrustedValidators = nil }, expErr: "trusted validator set in Header1 cannot be empty", }, { name: "invalid misbehaviour, header2 trusted val is nil", malleate: func() { misbehaviour.Header2.TrustedValidators = nil }, expErr: "trusted validator set in Header2 cannot be empty", }, { name: "invalid misbehaviour, chainID mismatch", malleate: func() { misbehaviour.Header2.Header.ChainID = "xxx" }, expErr: "headers must have identical chainIDs", }, { name: "valid misbehaviour, same headers", malleate: func() { misbehaviour.Header2 = misbehaviour.Header1 }, expErr: "", }, { name: "invalid misbehaviour, header2 height is greater", malleate: func() { // Update header2 so it has a height greater than header1 (3) // NOTE code generated by: // go run -C ./cmd/gen-block-signatures . -chainid=gno.land -header-time-shift=0 -height=4 -new-validators=0 -privkeys=JR6fAmf/x02hJxbgPlOUlEw82K1g5L6t7T0ochto3n/IQH0mFRpgSOtSqy3rxrjQmFKzT36uAKK7mHczlEvg0w==,uUowEMik5bYqOuxT8xXS6+vhmeJ1ogEoG7lUuKColILehBmNNm2bNvn7IQmuyrnkIhRFVI2XQ6tO4HWVxCFoOA== var ( apphash = hash("apphash-4") // priv=JR6fAmf/x02hJxbgPlOUlEw82K1g5L6t7T0ochto3n/IQH0mFRpgSOtSqy3rxrjQmFKzT36uAKK7mHczlEvg0w== val1 = tendermint.NewValidator("ZekR7Mi3r4Uewt3lPS3YmWPr81o=", "yEB9JhUaYEjrUqst68a40JhSs09+rgCiu5h3M5RL4NM=", 10) // priv=uUowEMik5bYqOuxT8xXS6+vhmeJ1ogEoG7lUuKColILehBmNNm2bNvn7IQmuyrnkIhRFVI2XQ6tO4HWVxCFoOA== val2 = tendermint.NewValidator("mdEIzF8KCHUrBT16vM7dhc/nZ4Q=", "3oQZjTZtmzb5+yEJrsq55CIURVSNl0OrTuB1lcQhaDg=", 10) valset = tendermint.NewValset(val1, val2) commitTimestamp = toTime("2025-09-25T07:55:57.306746166Z") newHeight = uint64(4) newTimestamp = time.Now() nextValset = tendermint.NewValset(val1, val2) trustedValset = tendermint.NewValset(val1, val2) trustedHeight = types.NewHeight(0, 2) signatures = []tendermint.CommitSig{ { BlockIDFlag: tendermint.BlockIDFlagCommit, ValidatorAddress: valset.Validators[0].Address, Timestamp: commitTimestamp, Signature: []byte("\xcb\x8f\x2f\x54\x34\xe9\xc7\xa5\x3e\xd4\x4b\x1d\x37\xbd\x69\xeb\x75\x01\x12\xbd\x11\x3b\x5d\xb1\x7f\xe1\xce\xd6\x8f\xd6\xf2\x79\x49\xd8\x20\xb3\x8c\x74\x35\xc5\x36\x37\x03\x21\x8c\x38\x2e\xf3\x1c\xd1\xfb\xee\xd1\xee\x8c\x0e\x16\x0e\x06\x0f\xdb\x4f\xbe\x03"), }, { BlockIDFlag: tendermint.BlockIDFlagCommit, ValidatorAddress: valset.Validators[1].Address, Timestamp: commitTimestamp, Signature: []byte("\xde\x94\xa7\x23\x40\x3d\xa4\xb6\x92\xb0\x39\xdc\x88\xb7\x22\x0d\x5d\x1f\x3e\xb0\xab\x09\xb8\x48\x4e\x5b\xcc\x68\xe1\xcf\x06\xff\xc5\x67\xe9\x5d\x6a\xd6\xd1\x5f\xf7\xf0\x28\x3f\x18\xd9\xf4\xc7\xd4\xd2\x30\x73\xbb\xe4\xde\x4b\xfe\x71\xd3\x05\x75\xe2\xf8\x0f"), }, } ) misbehaviour.Header2 = newMsgHeader( chainID, newTimestamp, apphash, newHeight, trustedHeight, valset, nextValset, trustedValset, signatures, ) }, expErr: "Header1 height is less than Header2 height (0/3 < 0/4)", }, { name: "invalid misbehabiour: header1 doesnt have 2/3 majority", malleate: func() { misbehaviour.Header1.Commit.Signatures[0] = tendermint.CommitSig{ BlockIDFlag: tendermint.BlockIDFlagAbsent, } }, expErr: "invalid header1 commit: validator set did not commit to header: not enough voting power, got 10, needed >13", }, { name: "invalid misbehabiour: header2 doesnt have 2/3 majority", malleate: func() { misbehaviour.Header2.Commit.Signatures[0] = tendermint.CommitSig{ BlockIDFlag: tendermint.BlockIDFlagAbsent, } }, expErr: "invalid header2 commit: validator set did not commit to header: not enough voting power, got 10, needed >13", }, { name: "invalid misbehabiour: header1 wrong signature", malleate: func() { misbehaviour.Header1.Commit.Signatures[0].Timestamp = time.Now() }, expErr: "invalid header1 commit: validator set did not commit to header: verify signature fail (#0)", }, { name: "invalid misbehabiour: header2 wrong signature", malleate: func() { misbehaviour.Header2.Commit.Signatures[0].Timestamp = time.Now() }, expErr: "invalid header2 commit: validator set did not commit to header: verify signature fail (#0)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var header1, header2 *tendermint.MsgHeader // NOTE code generated by: // go run -C ./cmd/gen-block-signatures . -chainid=gno.land -header-time-shift=0 -height=3 -new-validators=0 -privkeys=JR6fAmf/x02hJxbgPlOUlEw82K1g5L6t7T0ochto3n/IQH0mFRpgSOtSqy3rxrjQmFKzT36uAKK7mHczlEvg0w==,uUowEMik5bYqOuxT8xXS6+vhmeJ1ogEoG7lUuKColILehBmNNm2bNvn7IQmuyrnkIhRFVI2XQ6tO4HWVxCFoOA== { var ( apphash = hash("apphash-3") // priv=JR6fAmf/x02hJxbgPlOUlEw82K1g5L6t7T0ochto3n/IQH0mFRpgSOtSqy3rxrjQmFKzT36uAKK7mHczlEvg0w== val1 = tendermint.NewValidator("ZekR7Mi3r4Uewt3lPS3YmWPr81o=", "yEB9JhUaYEjrUqst68a40JhSs09+rgCiu5h3M5RL4NM=", 10) // priv=uUowEMik5bYqOuxT8xXS6+vhmeJ1ogEoG7lUuKColILehBmNNm2bNvn7IQmuyrnkIhRFVI2XQ6tO4HWVxCFoOA== val2 = tendermint.NewValidator("mdEIzF8KCHUrBT16vM7dhc/nZ4Q=", "3oQZjTZtmzb5+yEJrsq55CIURVSNl0OrTuB1lcQhaDg=", 10) valset = tendermint.NewValset(val1, val2) commitTimestamp = toTime("2025-09-25T07:55:57.306746166Z") newHeight = uint64(3) newTimestamp = time.Now() nextValset = tendermint.NewValset(val1, val2) trustedValset = tendermint.NewValset(val1, val2) trustedHeight = types.NewHeight(0, 2) signatures = []tendermint.CommitSig{ { BlockIDFlag: tendermint.BlockIDFlagCommit, ValidatorAddress: valset.Validators[0].Address, Timestamp: commitTimestamp, Signature: []byte("\x70\x47\xa3\x9d\x70\xe7\x8b\x61\x51\xb8\x76\xfb\x0a\xf1\xd8\xb9\x98\x54\x57\x68\xe5\x5a\x93\x9d\x6a\x4e\x1e\x0f\x1e\xa7\xce\x8c\x58\x93\xfc\xe7\x58\x85\xb2\x7a\x8d\xe1\x1f\xde\xda\x10\x0f\x85\xcd\x42\x0a\xa1\xff\x20\xb3\x8b\x7c\x20\xc6\x56\x83\xf9\xf5\x09"), }, { BlockIDFlag: tendermint.BlockIDFlagCommit, ValidatorAddress: valset.Validators[1].Address, Timestamp: commitTimestamp, Signature: []byte("\x80\x31\x76\x91\x4f\x71\x3e\xe5\x4f\xcf\xd1\x9e\xff\x49\x90\xbb\x09\x81\x22\x6d\xb0\x23\x85\xc3\x07\x49\x3a\x20\x75\x7e\x10\x4f\x98\x97\xb0\x0b\xa2\xdb\x49\x8c\x4c\x6d\xc6\x40\xd6\x6f\x16\xad\x0f\xf6\xff\xa6\x9f\x8e\x51\x95\xc1\x39\x78\x74\x24\x0e\x80\x02"), }, } ) header1 = newMsgHeader( chainID, newTimestamp, apphash, newHeight, trustedHeight, valset, nextValset, trustedValset, signatures, ) } // NOTE code generated by: // go run -C ./cmd/gen-block-signatures . -chainid=gno.land -header-time-shift=0 -height=2 -new-validators=0 -privkeys=JR6fAmf/x02hJxbgPlOUlEw82K1g5L6t7T0ochto3n/IQH0mFRpgSOtSqy3rxrjQmFKzT36uAKK7mHczlEvg0w==,uUowEMik5bYqOuxT8xXS6+vhmeJ1ogEoG7lUuKColILehBmNNm2bNvn7IQmuyrnkIhRFVI2XQ6tO4HWVxCFoOA== { var ( apphash = hash("apphash-2") // priv=JR6fAmf/x02hJxbgPlOUlEw82K1g5L6t7T0ochto3n/IQH0mFRpgSOtSqy3rxrjQmFKzT36uAKK7mHczlEvg0w== val1 = tendermint.NewValidator("ZekR7Mi3r4Uewt3lPS3YmWPr81o=", "yEB9JhUaYEjrUqst68a40JhSs09+rgCiu5h3M5RL4NM=", 10) // priv=uUowEMik5bYqOuxT8xXS6+vhmeJ1ogEoG7lUuKColILehBmNNm2bNvn7IQmuyrnkIhRFVI2XQ6tO4HWVxCFoOA== val2 = tendermint.NewValidator("mdEIzF8KCHUrBT16vM7dhc/nZ4Q=", "3oQZjTZtmzb5+yEJrsq55CIURVSNl0OrTuB1lcQhaDg=", 10) valset = tendermint.NewValset(val1, val2) commitTimestamp = toTime("2025-09-25T07:55:57.306746166Z") newHeight = uint64(2) newTimestamp = time.Now() nextValset = tendermint.NewValset(val1, val2) trustedValset = tendermint.NewValset(val1, val2) trustedHeight = types.NewHeight(0, 1) signatures = []tendermint.CommitSig{ { BlockIDFlag: tendermint.BlockIDFlagCommit, ValidatorAddress: valset.Validators[0].Address, Timestamp: commitTimestamp, Signature: []byte("\x05\x93\x46\x01\x51\x5e\x42\xd2\xdc\xef\xd4\x07\xc2\x13\x92\x03\xb2\x5c\x1a\xae\xf8\x45\x98\x5d\xea\xc1\x7a\xb8\xec\x8b\x48\x82\xf6\x76\xfe\xf6\xbd\x26\xc9\xb2\x59\x8c\xf3\xa1\xdc\x6e\xa5\xf5\xef\x3b\x7a\x5d\xa0\xa9\xb9\xcc\x8f\x62\xc7\xa6\x95\xae\xdc\x09"), }, { BlockIDFlag: tendermint.BlockIDFlagCommit, ValidatorAddress: valset.Validators[1].Address, Timestamp: commitTimestamp, Signature: []byte("\x8d\xe9\x5c\x39\xdf\x3f\x15\xe8\xa8\x20\x90\x6f\xa1\x2f\xf1\x04\xb6\x1e\xd1\xbf\x9b\xd3\xc9\x3f\x0b\xcc\x81\x76\xdc\x0f\xf7\x75\x0a\x5a\xed\x9f\x7d\xb9\x9e\x37\x3c\xcc\x5b\x38\x6d\x5d\x34\x32\x45\x85\x9d\xb0\x6c\x09\x49\x89\x39\x8f\xe8\x00\x82\x0f\x2f\x04"), }, } ) header2 = newMsgHeader( chainID, newTimestamp, apphash, newHeight, trustedHeight, valset, nextValset, trustedValset, signatures, ) } misbehaviour = &tendermint.Misbehaviour{ Header1: header1, Header2: header2, } if tt.malleate != nil { tt.malleate() } err := misbehaviour.ValidateBasic() if tt.expErr == "" && err != nil { t.Errorf("expected no error got %v", err) return } if tt.expErr != "" { if err == nil || !strings.Contains(err.Error(), tt.expErr) { t.Errorf("expected error %s, got %s", tt.expErr, err) } } }) } }