github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/crypto/merkle/proof_test.go (about) 1 package merkle 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 "github.com/badrootd/nibiru-cometbft/crypto/tmhash" 13 cmtcrypto "github.com/badrootd/nibiru-cometbft/proto/tendermint/crypto" 14 ) 15 16 const ProofOpDomino = "test:domino" 17 18 // Expects given input, produces given output. 19 // Like the game dominos. 20 type DominoOp struct { 21 key string // unexported, may be empty 22 Input string 23 Output string 24 } 25 26 func NewDominoOp(key, input, output string) DominoOp { 27 return DominoOp{ 28 key: key, 29 Input: input, 30 Output: output, 31 } 32 } 33 34 func (dop DominoOp) ProofOp() cmtcrypto.ProofOp { 35 dopb := cmtcrypto.DominoOp{ 36 Key: dop.key, 37 Input: dop.Input, 38 Output: dop.Output, 39 } 40 bz, err := dopb.Marshal() 41 if err != nil { 42 panic(err) 43 } 44 45 return cmtcrypto.ProofOp{ 46 Type: ProofOpDomino, 47 Key: []byte(dop.key), 48 Data: bz, 49 } 50 } 51 52 func (dop DominoOp) Run(input [][]byte) (output [][]byte, err error) { 53 if len(input) != 1 { 54 return nil, errors.New("expected input of length 1") 55 } 56 if string(input[0]) != dop.Input { 57 return nil, fmt.Errorf("expected input %v, got %v", 58 dop.Input, string(input[0])) 59 } 60 return [][]byte{[]byte(dop.Output)}, nil 61 } 62 63 func (dop DominoOp) GetKey() []byte { 64 return []byte(dop.key) 65 } 66 67 //---------------------------------------- 68 69 func TestProofOperators(t *testing.T) { 70 var err error 71 72 // ProofRuntime setup 73 // TODO test this somehow. 74 75 // ProofOperators setup 76 op1 := NewDominoOp("KEY1", "INPUT1", "INPUT2") 77 op2 := NewDominoOp("KEY2", "INPUT2", "INPUT3") 78 op3 := NewDominoOp("", "INPUT3", "INPUT4") 79 op4 := NewDominoOp("KEY4", "INPUT4", "OUTPUT4") 80 81 // Good 82 popz := ProofOperators([]ProofOperator{op1, op2, op3, op4}) 83 err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 84 assert.Nil(t, err) 85 err = popz.VerifyValue(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", bz("INPUT1")) 86 assert.Nil(t, err) 87 88 // BAD INPUT 89 err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1_WRONG")}) 90 assert.NotNil(t, err) 91 err = popz.VerifyValue(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", bz("INPUT1_WRONG")) 92 assert.NotNil(t, err) 93 94 // BAD KEY 1 95 err = popz.Verify(bz("OUTPUT4"), "/KEY3/KEY2/KEY1", [][]byte{bz("INPUT1")}) 96 assert.NotNil(t, err) 97 98 // BAD KEY 2 99 err = popz.Verify(bz("OUTPUT4"), "KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 100 assert.NotNil(t, err) 101 102 // BAD KEY 3 103 err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1/", [][]byte{bz("INPUT1")}) 104 assert.NotNil(t, err) 105 106 // BAD KEY 4 107 err = popz.Verify(bz("OUTPUT4"), "//KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 108 assert.NotNil(t, err) 109 110 // BAD KEY 5 111 err = popz.Verify(bz("OUTPUT4"), "/KEY2/KEY1", [][]byte{bz("INPUT1")}) 112 assert.NotNil(t, err) 113 114 // BAD OUTPUT 1 115 err = popz.Verify(bz("OUTPUT4_WRONG"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 116 assert.NotNil(t, err) 117 118 // BAD OUTPUT 2 119 err = popz.Verify(bz(""), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 120 assert.NotNil(t, err) 121 122 // BAD POPZ 1 123 popz = []ProofOperator{op1, op2, op4} 124 err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 125 assert.NotNil(t, err) 126 127 // BAD POPZ 2 128 popz = []ProofOperator{op4, op3, op2, op1} 129 err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 130 assert.NotNil(t, err) 131 132 // BAD POPZ 3 133 popz = []ProofOperator{} 134 err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")}) 135 assert.NotNil(t, err) 136 } 137 138 func bz(s string) []byte { 139 return []byte(s) 140 } 141 142 func TestProofValidateBasic(t *testing.T) { 143 testCases := []struct { 144 testName string 145 malleateProof func(*Proof) 146 errStr string 147 }{ 148 {"Good", func(sp *Proof) {}, ""}, 149 {"Negative Total", func(sp *Proof) { sp.Total = -1 }, "negative Total"}, 150 {"Negative Index", func(sp *Proof) { sp.Index = -1 }, "negative Index"}, 151 {"Invalid LeafHash", func(sp *Proof) { sp.LeafHash = make([]byte, 10) }, 152 "expected LeafHash size to be 32, got 10"}, 153 {"Too many Aunts", func(sp *Proof) { sp.Aunts = make([][]byte, MaxAunts+1) }, 154 "expected no more than 100 aunts, got 101"}, 155 {"Invalid Aunt", func(sp *Proof) { sp.Aunts[0] = make([]byte, 10) }, 156 "expected Aunts#0 size to be 32, got 10"}, 157 } 158 159 for _, tc := range testCases { 160 tc := tc 161 t.Run(tc.testName, func(t *testing.T) { 162 _, proofs := ProofsFromByteSlices([][]byte{ 163 []byte("apple"), 164 []byte("watermelon"), 165 []byte("kiwi"), 166 }) 167 tc.malleateProof(proofs[0]) 168 err := proofs[0].ValidateBasic() 169 if tc.errStr != "" { 170 assert.Contains(t, err.Error(), tc.errStr) 171 } 172 }) 173 } 174 } 175 func TestVoteProtobuf(t *testing.T) { 176 177 _, proofs := ProofsFromByteSlices([][]byte{ 178 []byte("apple"), 179 []byte("watermelon"), 180 []byte("kiwi"), 181 }) 182 testCases := []struct { 183 testName string 184 v1 *Proof 185 expPass bool 186 }{ 187 {"empty proof", &Proof{}, false}, 188 {"failure nil", nil, false}, 189 {"success", proofs[0], true}, 190 } 191 for _, tc := range testCases { 192 pb := tc.v1.ToProto() 193 194 v, err := ProofFromProto(pb) 195 if tc.expPass { 196 require.NoError(t, err) 197 require.Equal(t, tc.v1, v, tc.testName) 198 } else { 199 require.Error(t, err) 200 } 201 } 202 } 203 204 // TestVsa2022_100 verifies https://blog.verichains.io/p/vsa-2022-100-tendermint-forging-membership-proof 205 func TestVsa2022_100(t *testing.T) { 206 // a fake key-value pair and its hash 207 key := []byte{0x13} 208 value := []byte{0x37} 209 vhash := tmhash.Sum(value) 210 bz := new(bytes.Buffer) 211 _ = encodeByteSlice(bz, key) 212 _ = encodeByteSlice(bz, vhash) 213 kvhash := tmhash.Sum(append([]byte{0}, bz.Bytes()...)) 214 215 // the malicious `op` 216 op := NewValueOp( 217 key, 218 &Proof{LeafHash: kvhash}, 219 ) 220 221 // the nil root 222 var root []byte 223 224 assert.NotNil(t, ProofOperators{op}.Verify(root, "/"+string(key), [][]byte{value})) 225 }