Skip to content

Commit

Permalink
feat: add stricter checks for iavl spec and tendermint spec (#386)
Browse files Browse the repository at this point in the history
* refactor: deobfuscate, many thanks to Damian for the help!

* fix: test + build

* Update go/ops.go

* chore: update error message to be more informative

* chore: error wording

* fix: test

* test: add unit tests

* chore: update godocs as per review suggestion

* Update go/ops.go

Co-authored-by: Damian Nolan <[email protected]>

* refactor: add stricter checks for iavl spec and tendermint spec

* refactor: remove one line fn

* lint

---------

Co-authored-by: Damian Nolan <[email protected]>
  • Loading branch information
colin-axner and damiannolan authored Oct 23, 2024
1 parent c521320 commit 315aa71
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
36 changes: 36 additions & 0 deletions go/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,19 @@ func validateIavlOps(op opType, layerNum int) error {
// grab the length of the remainder of the prefix
remLen := r.Len()
if layerNum == 0 {
// leaf node
if remLen != 0 {
return fmt.Errorf("expected remaining prefix length to be 0, got: %d", remLen)
}
if height != 0 {
return fmt.Errorf("expected leaf node height to be 0, got: %d", remLen)
}
if size != 1 {
return fmt.Errorf("expected leaf node size to be 1, got: %d", remLen)
}
} else {
// inner node
//
// when the child comes from the left, the suffix if filled in
// prefix: height | size | version | length byte (1 remainder)
//
Expand All @@ -75,6 +84,26 @@ func validateIavlOps(op opType, layerNum int) error {
return nil
}

// validateTendermintOps validates the prefix to ensure it begins with []byte{1}.
func validateTendermintOps(op *InnerOp) error {
if len(op.Prefix) == 0 {
return fmt.Errorf("inner op prefix must not be empty")
}

innerPrefix := []byte{1}
if op.Suffix != nil {
if !bytes.Equal(op.Prefix, innerPrefix) {
return fmt.Errorf("expected inner op prefix: %v, got: %v", innerPrefix, op.Prefix)
}
}

if !bytes.HasPrefix(op.Prefix, innerPrefix) {
return fmt.Errorf("expected inner op prefix to begin with: %v, got: %v", innerPrefix, op.Prefix[:1])
}

return nil
}

// Apply will calculate the leaf hash given the key and value being proven
func (op *LeafOp) Apply(key []byte, value []byte) ([]byte, error) {
if len(key) == 0 {
Expand Down Expand Up @@ -168,6 +197,13 @@ func (op *InnerOp) CheckAgainstSpec(spec *ProofSpec, b int) error {
}
}

if spec.SpecEquals(TendermintSpec) {
err := validateTendermintOps(op)
if err != nil {
return err
}
}

leafPrefix := spec.LeafSpec.Prefix
if bytes.HasPrefix(op.Prefix, leafPrefix) {
return fmt.Errorf("inner Prefix starts with %X", leafPrefix)
Expand Down
71 changes: 71 additions & 0 deletions go/ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,77 @@ func TestValidateIavlOps(t *testing.T) {
}
}

func TestValidateTendermintOps(t *testing.T) {
var op *InnerOp
cases := []struct {
name string
malleate func()
expError error
}{
{
"success: valid prefix when suffix populated",
func() {},
nil,
},
{
"success: valid prefix when suffix empty",
func() {
op.Prefix = []byte{1, 2}
op.Suffix = nil
},
nil,
},
{
"failure: empty prefix and suffix",
func() {
op.Prefix = nil
op.Suffix = nil
},
errors.New("inner op prefix must not be empty"),
},
{
"failure: invalid prefix when suffix populated",
func() {
op.Prefix = []byte{0}
op.Suffix = []byte{1}
},
fmt.Errorf("expected inner op prefix: %v, got: %v", []byte{1}, []byte{0}),
},
{
"failure: invalid prefix when suffix empty",
func() {
op.Prefix = []byte{2, 1}
op.Suffix = nil
},
fmt.Errorf("expected inner op prefix to begin with: %v, got: %v", []byte{1}, []byte{2}),
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
op = &InnerOp{
Hash: HashOp_SHA256,
Prefix: []byte{1},
Suffix: []byte{1},
}

tc.malleate()

err := validateTendermintOps(op)
if tc.expError == nil && err != nil {
t.Fatal(err)
}

if tc.expError != nil && err.Error() != tc.expError.Error() {
t.Fatalf("expected: %v, got: %v", tc.expError, err)
}
})

}
}

func TestLeafOp(t *testing.T) {
cases := LeafOpTestData(t)

Expand Down

0 comments on commit 315aa71

Please sign in to comment.