From 568f0e2d62987a29c65db9b8232b4242fd9d2b13 Mon Sep 17 00:00:00 2001 From: CHAMI Rachid Date: Fri, 2 Aug 2024 17:48:19 +0200 Subject: [PATCH 1/7] fix: check the correctness of the leaf ranges (#268) ## Overview Closes: https://github.com/celestiaorg/nmt/issues/267 ## Summary by CodeRabbit - **New Features** - Strengthened error handling for subtree root verification and leaf range calculations to improve robustness against invalid inputs. - **Bug Fixes** - Enhanced validation processes to prevent incorrect operations and ensure reliable range processing. - **Tests** - Refined test cases to focus on relevant scenarios and added new tests for improved coverage of edge cases related to error handling. --- proof.go | 12 +++++++- proof_test.go | 77 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/proof.go b/proof.go index bde67237..b260e179 100644 --- a/proof.go +++ b/proof.go @@ -528,6 +528,10 @@ func (proof Proof) VerifySubtreeRootInclusion(nth *NmtHasher, subtreeRoots [][]b return popIfNonEmpty(&subtreeRoots), nil } + if end-start == 1 { + return nil, fmt.Errorf("the provided range [%d, %d) does not reference a valid inner node", proof.start, proof.end) + } + // Recursively get left and right subtree k := getSplitPoint(end - start) left, err := computeRoot(start, start+k) @@ -634,7 +638,13 @@ func nextLeafRange(currentStart, currentEnd, subtreeWidth int) (LeafRange, error if err != nil { return LeafRange{}, err } - return LeafRange{Start: currentStart, End: currentStart + currentRange}, nil + rangeEnd := currentStart + currentRange + idealTreeSize := nextSubtreeSize(uint64(currentStart), uint64(rangeEnd)) + if currentStart+idealTreeSize != rangeEnd { + // this will happen if the calculated range does not correctly reference an inner node in the tree. + return LeafRange{}, fmt.Errorf("provided subtree width %d doesn't allow creating a valid leaf range [%d, %d)", subtreeWidth, currentStart, rangeEnd) + } + return LeafRange{Start: currentStart, End: rangeEnd}, nil } // largestPowerOfTwo calculates the largest power of two diff --git a/proof_test.go b/proof_test.go index b2ea98fe..cb208cdf 100644 --- a/proof_test.go +++ b/proof_test.go @@ -1332,18 +1332,6 @@ func TestNextLeafRange(t *testing.T) { subtreeRootMaximumLeafRange: 8, expectedRange: LeafRange{Start: 4, End: 8}, }, - { - currentStart: 4, - currentEnd: 20, - subtreeRootMaximumLeafRange: 16, - expectedRange: LeafRange{Start: 4, End: 20}, - }, - { - currentStart: 4, - currentEnd: 20, - subtreeRootMaximumLeafRange: 1, - expectedRange: LeafRange{Start: 4, End: 5}, - }, { currentStart: 4, currentEnd: 20, @@ -1356,12 +1344,6 @@ func TestNextLeafRange(t *testing.T) { subtreeRootMaximumLeafRange: 4, expectedRange: LeafRange{Start: 4, End: 8}, }, - { - currentStart: 4, - currentEnd: 20, - subtreeRootMaximumLeafRange: 8, - expectedRange: LeafRange{Start: 4, End: 12}, - }, { currentStart: 0, currentEnd: 1, @@ -1392,6 +1374,36 @@ func TestNextLeafRange(t *testing.T) { subtreeRootMaximumLeafRange: 0, expectError: true, }, + { // A range not referencing any inner node + currentStart: 1, + currentEnd: 3, + subtreeRootMaximumLeafRange: 4, + expectError: true, + }, + { // A range not referencing any inner node + currentStart: 1, + currentEnd: 5, + subtreeRootMaximumLeafRange: 4, + expectError: true, + }, + { // A range not referencing any inner node + currentStart: 1, + currentEnd: 6, + subtreeRootMaximumLeafRange: 4, + expectError: true, + }, + { // A range not referencing any inner node + currentStart: 1, + currentEnd: 7, + subtreeRootMaximumLeafRange: 4, + expectError: true, + }, + { // A range not referencing any inner node + currentStart: 2, + currentEnd: 8, + subtreeRootMaximumLeafRange: 4, + expectError: true, + }, } for _, tt := range tests { @@ -1798,7 +1810,6 @@ func TestVerifySubtreeRootInclusion(t *testing.T) { root: root, expectError: true, }, - { proof: func() Proof { p, err := tree.ProveRange(0, 8) @@ -1828,3 +1839,31 @@ func TestVerifySubtreeRootInclusion(t *testing.T) { }) } } + +// TestVerifySubtreeRootInclusion_infiniteRecursion is motivated by a failing test +// case in celestia-node +func TestVerifySubtreeRootInclusion_infiniteRecursion(t *testing.T) { + namespaceIDs := bytes.Repeat([]byte{1}, 64) + tree := exampleNMT(1, true, namespaceIDs...) + root, err := tree.Root() + require.NoError(t, err) + + nmthasher := tree.treeHasher + hasher := nmthasher.(*NmtHasher) + subtreeRoot, err := tree.ComputeSubtreeRoot(0, 4) + require.NoError(t, err) + subtreeRoots := [][]byte{subtreeRoot, subtreeRoot, subtreeRoot, subtreeRoot, subtreeRoot, subtreeRoot, subtreeRoot} + subtreeWidth := 8 + + proof, err := tree.ProveRange(19, 64) + require.NoError(t, err) + + require.NotPanics(t, func() { + // This previously hits: + // runtime: goroutine stack exceeds 1000000000-byte limit + // runtime: sp=0x14020160480 stack=[0x14020160000, 0x14040160000] + // fatal error: stack overflow + _, err = proof.VerifySubtreeRootInclusion(hasher, subtreeRoots, subtreeWidth, root) + require.Error(t, err) + }) +} From cce31e890f382c1b90241a7340327f76c252a0cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 19:32:58 +0200 Subject: [PATCH 2/7] chore(deps): bump github.com/tidwall/gjson from 1.17.1 to 1.17.3 (#271) Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.17.1 to 1.17.3.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/tidwall/gjson&package-manager=go_modules&previous-version=1.17.1&new-version=1.17.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1aed400e..a5bba77a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/google/gofuzz v1.2.0 github.com/stretchr/testify v1.9.0 - github.com/tidwall/gjson v1.17.1 + github.com/tidwall/gjson v1.17.3 ) require ( diff --git a/go.sum b/go.sum index 05a31510..dd3dfd09 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= -github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= +github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= From 29e9433bd79eed40462000626ef987c91b0cf523 Mon Sep 17 00:00:00 2001 From: CHAMI Rachid Date: Tue, 27 Aug 2024 18:23:18 +0200 Subject: [PATCH 3/7] docs: add comment to leaf case in verify subtree roots inclusion (#272) ## Overview ## Summary by CodeRabbit - **Documentation** - Enhanced clarity of error handling in the verification process by adding explanatory comments regarding invalid ranges. --------- Co-authored-by: Rootul P --- proof.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proof.go b/proof.go index b260e179..70d81ab1 100644 --- a/proof.go +++ b/proof.go @@ -529,6 +529,9 @@ func (proof Proof) VerifySubtreeRootInclusion(nth *NmtHasher, subtreeRoots [][]b } if end-start == 1 { + // At this level, we reached a leaf, but we couldn't find any range corresponding + // to needed leaf [start, end). + // This means that the initial provided [start, end) range was invalid. return nil, fmt.Errorf("the provided range [%d, %d) does not reference a valid inner node", proof.start, proof.end) } From d6a34366eee7898ba2ca44814d9b14cf4d07bbfd Mon Sep 17 00:00:00 2001 From: CHAMI Rachid Date: Wed, 25 Sep 2024 20:16:44 +0200 Subject: [PATCH 4/7] ci: support releasing proto definitions to BSR (#274) Adds support for pushing the proto definitions to BSR ## Summary by CodeRabbit - **New Features** - Introduced a continuous integration (CI) workflow for automated checks on Protocol Buffers. - Added a new module configuration for managing protocol buffers. - **Documentation** - New workflow file created to outline CI processes. - Configuration file established for module versioning and structure. --------- Co-authored-by: Rootul P --- .github/workflows/buf-ci.yaml | 29 +++++++++++++++++++++++++++++ buf.yaml | 12 ++++++++++++ pb/proof.proto | 10 +++++----- 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/buf-ci.yaml create mode 100644 buf.yaml diff --git a/.github/workflows/buf-ci.yaml b/.github/workflows/buf-ci.yaml new file mode 100644 index 00000000..6d22c7f2 --- /dev/null +++ b/.github/workflows/buf-ci.yaml @@ -0,0 +1,29 @@ +name: buf-ci +on: + push: + branches: + - main + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" + pull_request: +permissions: + contents: read + pull-requests: write +jobs: + buf: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bufbuild/buf-action@v1 + with: + token: ${{ secrets.BUF_TOKEN }} + input: "pb" + breaking-against: 'https://github.com/celestiaorg/nmt.git#branch=main,ref=HEAD~1,subdir=pb' + # Run breaking change, lint, and format checks for Protobuf sources against all branches, + # 'pb' subdirectory, then push to the Buf Schema Registry once validated + lint: true + format: true + breaking: true diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 00000000..da4e3b12 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,12 @@ +version: v2 +modules: + - path: pb + name: buf.build/celestia/nmt +lint: + ignore_only: + PACKAGE_DIRECTORY_MATCH: + # ignoring because fixing means we will do a breaking change + - pb/proof.proto + PACKAGE_VERSION_SUFFIX: + # ignoring because fixing means we will do a breaking change + - pb/proof.proto diff --git a/pb/proof.proto b/pb/proof.proto index 431685a6..9b48d392 100644 --- a/pb/proof.proto +++ b/pb/proof.proto @@ -1,4 +1,4 @@ -syntax="proto3"; +syntax = "proto3"; package proof.pb; @@ -6,10 +6,10 @@ option go_package = "github.com/celestiaorg/nmt/pb"; message Proof { // Start index of the leaves that match the queried namespace.ID. - int64 start = 1; + int64 start = 1; // End index (non-inclusive) of the leaves that match the queried // namespace.ID. - int64 end = 2; + int64 end = 2; // Nodes hold the tree nodes necessary for the Merkle range proof. repeated bytes nodes = 3; // leaf_hash contains the namespace.ID if NMT does not have it and @@ -18,5 +18,5 @@ message Proof { bytes leaf_hash = 4; // The is_max_namespace_ignored flag influences the calculation of the // namespace ID range for intermediate nodes in the tree. - bool is_max_namespace_ignored=5; -} \ No newline at end of file + bool is_max_namespace_ignored = 5; +} From b2a01871d63e87c9766b11f93e482c954c2d6494 Mon Sep 17 00:00:00 2001 From: CHAMI Rachid Date: Mon, 7 Oct 2024 21:10:24 +0200 Subject: [PATCH 5/7] fix(ci): update the Buf CI to push tags to the Buf Schema Registry (#280) This PR fixes the push of a new tag to BSR. Tested in my fork in here: https://buf.build/chamirachid/nmt/labels ## Summary by CodeRabbit - **New Features** - Introduced a new GitHub Actions workflow for continuous integration of Buf-related changes. - Added a workflow for automating the release of protobuf definitions to the Buf Schema Registry. - New script for downloading the specified version of the `buf` binary. - **Bug Fixes** - Removed the outdated `buf-ci` workflow to streamline processes. --- .github/workflows/buf-ci.yaml | 29 ----------------------------- .github/workflows/buf-ci.yml | 22 ++++++++++++++++++++++ .github/workflows/buf-release.yml | 23 +++++++++++++++++++++++ 3 files changed, 45 insertions(+), 29 deletions(-) delete mode 100644 .github/workflows/buf-ci.yaml create mode 100644 .github/workflows/buf-ci.yml create mode 100644 .github/workflows/buf-release.yml diff --git a/.github/workflows/buf-ci.yaml b/.github/workflows/buf-ci.yaml deleted file mode 100644 index 6d22c7f2..00000000 --- a/.github/workflows/buf-ci.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: buf-ci -on: - push: - branches: - - main - tags: - - "v[0-9]+.[0-9]+.[0-9]+" - - "v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+" - - "v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+" - - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" - pull_request: -permissions: - contents: read - pull-requests: write -jobs: - buf: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: bufbuild/buf-action@v1 - with: - token: ${{ secrets.BUF_TOKEN }} - input: "pb" - breaking-against: 'https://github.com/celestiaorg/nmt.git#branch=main,ref=HEAD~1,subdir=pb' - # Run breaking change, lint, and format checks for Protobuf sources against all branches, - # 'pb' subdirectory, then push to the Buf Schema Registry once validated - lint: true - format: true - breaking: true diff --git a/.github/workflows/buf-ci.yml b/.github/workflows/buf-ci.yml new file mode 100644 index 00000000..7d3a62d7 --- /dev/null +++ b/.github/workflows/buf-ci.yml @@ -0,0 +1,22 @@ +name: buf-ci +on: + push: + branches: + - main + pull_request: +permissions: + contents: read + pull-requests: write +jobs: + buf: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bufbuild/buf-setup-action@v1 + - uses: bufbuild/buf-breaking-action@v1 + with: + input: pb + against: 'https://github.com/celestiaorg/nmt.git#branch=main,subdir=pb' + - uses: bufbuild/buf-lint-action@v1 + with: + input: pb diff --git a/.github/workflows/buf-release.yml b/.github/workflows/buf-release.yml new file mode 100644 index 00000000..98832465 --- /dev/null +++ b/.github/workflows/buf-release.yml @@ -0,0 +1,23 @@ +name: buf-release +on: + push: + tags: + - "v*" +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bufbuild/buf-setup-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + version: "1.44.0" + # Push the protobuf definitions to the BSR + - uses: bufbuild/buf-push-action@v1 + with: + buf_token: ${{ secrets.BUF_TOKEN }} + - name: "push the tag label to BSR" + run: | + set -euo pipefail + echo ${{ secrets.BUF_TOKEN }} | buf registry login --token-stdin + buf push --label ${{ github.ref_name }} From c1a9cfc042eb30384ba8f6187258cf285eff86be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:46:00 -0400 Subject: [PATCH 6/7] chore(deps): bump github.com/tidwall/gjson from 1.17.3 to 1.18.0 (#277) Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.17.3 to 1.18.0.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/tidwall/gjson&package-manager=go_modules&previous-version=1.17.3&new-version=1.18.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a5bba77a..b99ee4eb 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/google/gofuzz v1.2.0 github.com/stretchr/testify v1.9.0 - github.com/tidwall/gjson v1.17.3 + github.com/tidwall/gjson v1.18.0 ) require ( diff --git a/go.sum b/go.sum index dd3dfd09..06b10d63 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= -github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= From 62ec215323bb32cc44bcb9d1ecd1606c7e12a906 Mon Sep 17 00:00:00 2001 From: CHAMI Rachid Date: Tue, 8 Oct 2024 08:40:30 +0200 Subject: [PATCH 7/7] chore: fix ci linter failing (#276) fixes CI linter --- proof.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proof.go b/proof.go index 70d81ab1..57b38b9f 100644 --- a/proof.go +++ b/proof.go @@ -520,7 +520,7 @@ func (proof Proof) VerifySubtreeRootInclusion(nth *NmtHasher, subtreeRoots [][]b } if len(ranges) == 0 { - return nil, fmt.Errorf(fmt.Sprintf("expected to have a subtree root for range [%d, %d)", start, end)) + return nil, fmt.Errorf("expected to have a subtree root for range [%d, %d)", start, end) } if ranges[0].Start == start && ranges[0].End == end {