github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/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/celestia-core/crypto/tmhash" 13 cmtcrypto "github.com/badrootd/celestia-core/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 TestProofOperatorsFromKeys(t *testing.T) { 139 var err error 140 141 // ProofRuntime setup 142 // TODO test this somehow. 143 144 // ProofOperators setup 145 op1 := NewDominoOp("KEY1", "INPUT1", "INPUT2") 146 op2 := NewDominoOp("KEY%2", "INPUT2", "INPUT3") 147 op3 := NewDominoOp("", "INPUT3", "INPUT4") 148 op4 := NewDominoOp("KEY/4", "INPUT4", "OUTPUT4") 149 150 // add characters to the keys that would otherwise result in bad keypath if 151 // processed 152 keys1 := [][]byte{bz("KEY/4"), bz("KEY%2"), bz("KEY1")} 153 badkeys1 := [][]byte{bz("WrongKey"), bz("KEY%2"), bz("KEY1")} 154 keys2 := [][]byte{bz("KEY3"), bz("KEY%2"), bz("KEY1")} 155 keys3 := [][]byte{bz("KEY2"), bz("KEY1")} 156 157 // Good 158 popz := ProofOperators([]ProofOperator{op1, op2, op3, op4}) 159 err = popz.VerifyFromKeys(bz("OUTPUT4"), keys1, [][]byte{bz("INPUT1")}) 160 assert.NoError(t, err) 161 162 // BAD INPUT 163 err = popz.VerifyFromKeys(bz("OUTPUT4"), keys1, [][]byte{bz("INPUT1_WRONG")}) 164 assert.Error(t, err) 165 166 // BAD KEY 1 167 err = popz.VerifyFromKeys(bz("OUTPUT4"), keys2, [][]byte{bz("INPUT1")}) 168 assert.Error(t, err) 169 170 // BAD KEY 2 171 err = popz.VerifyFromKeys(bz("OUTPUT4"), badkeys1, [][]byte{bz("INPUT1")}) 172 assert.Error(t, err) 173 174 // BAD KEY 5 175 err = popz.VerifyFromKeys(bz("OUTPUT4"), keys3, [][]byte{bz("INPUT1")}) 176 assert.Error(t, err) 177 178 // BAD OUTPUT 1 179 err = popz.VerifyFromKeys(bz("OUTPUT4_WRONG"), keys1, [][]byte{bz("INPUT1")}) 180 assert.Error(t, err) 181 182 // BAD OUTPUT 2 183 err = popz.VerifyFromKeys(bz(""), keys1, [][]byte{bz("INPUT1")}) 184 assert.Error(t, err) 185 186 // BAD POPZ 1 187 popz = []ProofOperator{op1, op2, op4} 188 err = popz.VerifyFromKeys(bz("OUTPUT4"), keys1, [][]byte{bz("INPUT1")}) 189 assert.Error(t, err) 190 191 // BAD POPZ 2 192 popz = []ProofOperator{op4, op3, op2, op1} 193 err = popz.VerifyFromKeys(bz("OUTPUT4"), keys1, [][]byte{bz("INPUT1")}) 194 assert.Error(t, err) 195 196 // BAD POPZ 3 197 popz = []ProofOperator{} 198 err = popz.VerifyFromKeys(bz("OUTPUT4"), keys1, [][]byte{bz("INPUT1")}) 199 assert.Error(t, err) 200 } 201 202 func bz(s string) []byte { 203 return []byte(s) 204 } 205 206 func TestProofValidateBasic(t *testing.T) { 207 testCases := []struct { 208 testName string 209 malleateProof func(*Proof) 210 errStr string 211 }{ 212 {"Good", func(sp *Proof) {}, ""}, 213 {"Negative Total", func(sp *Proof) { sp.Total = -1 }, "negative Total"}, 214 {"Negative Index", func(sp *Proof) { sp.Index = -1 }, "negative Index"}, 215 {"Invalid LeafHash", func(sp *Proof) { sp.LeafHash = make([]byte, 10) }, 216 "expected LeafHash size to be 32, got 10"}, 217 {"Too many Aunts", func(sp *Proof) { sp.Aunts = make([][]byte, MaxAunts+1) }, 218 "expected no more than 100 aunts, got 101"}, 219 {"Invalid Aunt", func(sp *Proof) { sp.Aunts[0] = make([]byte, 10) }, 220 "expected Aunts#0 size to be 32, got 10"}, 221 } 222 223 for _, tc := range testCases { 224 tc := tc 225 t.Run(tc.testName, func(t *testing.T) { 226 _, proofs := ProofsFromByteSlices([][]byte{ 227 []byte("apple"), 228 []byte("watermelon"), 229 []byte("kiwi"), 230 }) 231 tc.malleateProof(proofs[0]) 232 err := proofs[0].ValidateBasic() 233 if tc.errStr != "" { 234 assert.Contains(t, err.Error(), tc.errStr) 235 } 236 }) 237 } 238 } 239 func TestVoteProtobuf(t *testing.T) { 240 241 _, proofs := ProofsFromByteSlices([][]byte{ 242 []byte("apple"), 243 []byte("watermelon"), 244 []byte("kiwi"), 245 }) 246 testCases := []struct { 247 testName string 248 v1 *Proof 249 expPass bool 250 }{ 251 {"empty proof", &Proof{}, false}, 252 {"failure nil", nil, false}, 253 {"success", proofs[0], true}, 254 } 255 for _, tc := range testCases { 256 pb := tc.v1.ToProto() 257 258 v, err := ProofFromProto(pb) 259 if tc.expPass { 260 require.NoError(t, err) 261 require.Equal(t, tc.v1, v, tc.testName) 262 } else { 263 require.Error(t, err) 264 } 265 } 266 } 267 268 // TestVsa2022_100 verifies https://blog.verichains.io/p/vsa-2022-100-tendermint-forging-membership-proof 269 func TestVsa2022_100(t *testing.T) { 270 // a fake key-value pair and its hash 271 key := []byte{0x13} 272 value := []byte{0x37} 273 vhash := tmhash.Sum(value) 274 bz := new(bytes.Buffer) 275 _ = encodeByteSlice(bz, key) 276 _ = encodeByteSlice(bz, vhash) 277 kvhash := tmhash.Sum(append([]byte{0}, bz.Bytes()...)) 278 279 // the malicious `op` 280 op := NewValueOp( 281 key, 282 &Proof{LeafHash: kvhash}, 283 ) 284 285 // the nil root 286 var root []byte 287 288 assert.NotNil(t, ProofOperators{op}.Verify(root, "/"+string(key), [][]byte{value})) 289 }