diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 98280c4e5d..8074875fea 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,6 +7,6 @@ # global owners are only requested if there isn't a more specific # codeowner specified below. For this reason, the global codeowners # are often repeated in package-level definitions. -* @renaynay @Wondertan @vgonkivs @distractedm1nd @walldiss @cristaloleg +* @renaynay @Wondertan @vgonkivs @walldiss @cristaloleg docs/adr @adlerjohn @liamsi diff --git a/.github/dependabot.yml b/.github/dependabot.yml index edcd86e11d..b0c7cbbd37 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,6 +9,11 @@ updates: open-pull-requests-limit: 10 labels: - kind:deps + groups: + patch-updates: + applies-to: version-updates + update-types: + - "patch" - package-ecosystem: gomod directory: "/" schedule: @@ -25,6 +30,10 @@ updates: - dependency-name: "*otel*" update-types: ["version-update:semver-patch"] groups: + patch-updates: + applies-to: version-updates + update-types: + - "patch" otel: patterns: - "go.opentelemetry.io/otel*" @@ -37,3 +46,8 @@ updates: open-pull-requests-limit: 10 labels: - kind:deps + groups: + patch-updates: + applies-to: version-updates + update-types: + - "patch" diff --git a/.github/workflows/ci_release.yml b/.github/workflows/ci_release.yml index f45ee71d66..df3abc8329 100644 --- a/.github/workflows/ci_release.yml +++ b/.github/workflows/ci_release.yml @@ -1,24 +1,20 @@ name: CI and Release + on: merge_group: push: branches: - main + tags: + - '**' release: types: [published] pull_request: workflow_dispatch: inputs: - version: - # Friendly description to be shown in the UI instead of 'name' - description: "Semver type of new version (major / minor / patch)" - # Input has to be provided for the workflow to run + run-on-tag: + description: 'Tag for release' required: true - type: choice - options: - - patch - - minor - - major jobs: # set up go version for use through pipelines, setting @@ -54,7 +50,7 @@ jobs: # Dockerfile Linting hadolint: - uses: celestiaorg/.github/.github/workflows/reusable_dockerfile_lint.yml@v0.4.3 # yamllint disable-line rule:line-length + uses: celestiaorg/.github/.github/workflows/reusable_dockerfile_lint.yml@v0.4.5 # yamllint disable-line rule:line-length with: dockerfile: Dockerfile @@ -62,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: celestiaorg/.github/.github/actions/yamllint@v0.4.3 + - uses: celestiaorg/.github/.github/actions/yamllint@v0.4.5 markdown-lint: name: Markdown Lint @@ -82,41 +78,31 @@ jobs: with: go-version: ${{ needs.setup.outputs.go-version }} - # If this was a workflow dispatch event, we need to generate and push a tag - # for goreleaser to grab - version_bump: - needs: [hadolint, yamllint, markdown-lint, go-ci, setup] - runs-on: ubuntu-latest - permissions: "write-all" - steps: - - uses: actions/checkout@v4 - - - name: Bump version and push tag - # Placing the if condition here is a workaround for needing to block - # on this step during workflow dispatch events but the step not - # needing to run on tags. If we had the if condition on the full - # version_bump section, it would skip and not run, which would result - # in goreleaser not running either. - if: ${{ github.event_name == 'workflow_dispatch' }} - uses: mathieudutour/github-tag-action@v6.2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - default_bump: ${{ inputs.version }} - release_branches: ${{ needs.setup.outputs.branch }} - # Generate the release with goreleaser to include pre-built binaries goreleaser: - needs: [version_bump, setup] + needs: [setup] runs-on: ubuntu-latest if: | - github.event_name == 'workflow_dispatch' || - github.event_name == 'release' + github.event_name == 'release' || + github.event_name == 'workflow_dispatch' permissions: "write-all" steps: - uses: actions/checkout@v4 - run: git fetch --force --tags + - name: Set LATEST_TAG for release + if: github.event_name == 'release' + run: echo "LATEST_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + + - name: Set LATEST_TAG for workflow_dispatch + if: github.event_name == 'workflow_dispatch' + run: echo "LATEST_TAG=${{ inputs.run-on-tag }}" >> $GITHUB_ENV + + - name: Checkout a given tag + if: github.event_name == 'workflow_dispatch' + run: git checkout ${{ inputs.run-on-tag }} + - uses: actions/setup-go@v5 with: go-version: ${{ needs.setup.outputs.go-version }} @@ -137,10 +123,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + GORELEASER_CURRENT_TAG: ${{ env.LATEST_TAG }} upload-docs: - needs: goreleaser - if: github.event_name == 'release' + needs: [setup] + if: | + github.event_name == 'release' || + github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest permissions: contents: write @@ -148,6 +137,23 @@ jobs: - uses: actions/checkout@v4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - run: git fetch --force --tags + + - name: Set LATEST_TAG for release + if: github.event_name == 'release' + run: echo "LATEST_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + + - name: Set LATEST_TAG for workflow_dispatch + if: github.event_name == 'workflow_dispatch' + run: echo "LATEST_TAG=${{ inputs.run-on-tag }}" >> $GITHUB_ENV + + - name: Checkout a given tag + if: github.event_name == 'workflow_dispatch' + run: git checkout ${{ inputs.run-on-tag }} + - run: | make openrpc-gen > openrpc.json - gh release upload ${{github.event.release.tag_name}} openrpc.json + gh release upload "$LATEST_TAG" openrpc.json + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/create_release_tracking_epic.yml b/.github/workflows/create_release_tracking_epic.yml deleted file mode 100644 index 2fa90be14a..0000000000 --- a/.github/workflows/create_release_tracking_epic.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Create Release Tracking Epic - -# This workflow creates an EPIC in the devops repo and notifies the devops team -# on slack for tracking the deployment of a release to testnets and mainnet. -on: - release: - types: [released] -jobs: - trigger_issue: - uses: celestiaorg/.github/.github/workflows/reusable_create_release_tracking_epic.yml@v0.4.3 - secrets: inherit - with: - release-repo: ${{ github.repository }} - release-version: ${{ github.event.release.tag_name }} diff --git a/.github/workflows/docker-build-publish.yml b/.github/workflows/docker-build-publish.yml index 3e60ae5184..88a616c998 100644 --- a/.github/workflows/docker-build-publish.yml +++ b/.github/workflows/docker-build-publish.yml @@ -7,18 +7,22 @@ on: 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]+" + - "v*" pull_request: + workflow_dispatch: + inputs: + ref: + description: "The checkout reference (ie tag, branch, sha)" + required: true + type: string jobs: docker-security-build: permissions: contents: write packages: write - uses: celestiaorg/.github/.github/workflows/reusable_dockerfile_pipeline.yml@v0.4.3 # yamllint disable-line rule:line-length + uses: celestiaorg/.github/.github/workflows/reusable_dockerfile_pipeline.yml@v0.5.0 # yamllint disable-line rule:line-length with: dockerfile: Dockerfile + checkout_ref: ${{ github.event.inputs.ref }} secrets: inherit diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index cc1196fccf..79b8d24f64 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -128,3 +128,18 @@ jobs: - name: run sync tests run: make test-integration SHORT=true TAGS=sync + + pruning_tests: + name: Integration Tests Sync + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: set up go + uses: actions/setup-go@v5 + with: + go-version: ${{ inputs.go-version }} + + - name: run sync tests + run: make test-integration SHORT=true TAGS=pruning diff --git a/.goreleaser.yaml b/.goreleaser.yaml index e95ced39ba..6651ff0dc8 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,3 +1,4 @@ +version: 2 # This is an example .goreleaser.yml file with some sensible defaults. # Make sure to check the documentation at https://goreleaser.com before: @@ -67,5 +68,7 @@ changelog: - "^test:" release: prerelease: auto + mode: keep-existing git: prerelease_suffix: "-rc*" + tag_sort: -version:creatordate diff --git a/Dockerfile b/Dockerfile index 84acbee8b4..c99d372ebb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM docker.io/golang:1.23-alpine3.20 as builder +FROM --platform=$BUILDPLATFORM docker.io/golang:1.23-alpine3.20 AS builder ARG TARGETPLATFORM ARG BUILDPLATFORM @@ -21,9 +21,11 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN uname -a &&\ - CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ - make build && make cel-key +RUN uname -a && \ + export CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} && \ + make build && \ + make cel-key && \ + make cel-shed FROM docker.io/alpine:3.20.2 @@ -34,8 +36,8 @@ ARG USER_NAME=celestia ENV CELESTIA_HOME=/home/${USER_NAME} # Default node type can be overwritten in deployment manifest -ENV NODE_TYPE bridge -ENV P2P_NETWORK mocha +ENV NODE_TYPE=bridge +ENV P2P_NETWORK=mocha # hadolint ignore=DL3018 RUN uname -a &&\ @@ -54,6 +56,7 @@ RUN uname -a &&\ # Copy in the binary COPY --from=builder /src/build/celestia /bin/celestia COPY --from=builder /src/./cel-key /bin/cel-key +COPY --from=builder /src/./cel-shed /bin/cel-shed COPY --chown=${USER_NAME}:${USER_NAME} docker/entrypoint.sh /opt/entrypoint.sh diff --git a/Makefile b/Makefile index c29835f7e4..8c1d06f0a8 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,9 @@ ifeq ($(SHORT),true) else INTEGRATION_RUN_LENGTH = endif + +include celestia-node.mk + ## help: Get more info on make commands. help: Makefile @echo " Choose a command run in "$(PROJECTNAME)":" @@ -163,13 +166,14 @@ PB_CORE=$(shell go list -f {{.Dir}} -m github.com/tendermint/tendermint) PB_GOGO=$(shell go list -f {{.Dir}} -m github.com/gogo/protobuf) PB_CELESTIA_APP=$(shell go list -f {{.Dir}} -m github.com/celestiaorg/celestia-app) PB_NMT=$(shell go list -f {{.Dir}} -m github.com/celestiaorg/nmt) +PB_NODE=$(shell pwd) ## pb-gen: Generate protobuf code for all /pb/*.proto files in the project. pb-gen: @echo '--> Generating protobuf' @for dir in $(PB_PKGS); \ do for file in `find $$dir -type f -name "*.proto"`; \ - do protoc -I=. -I=${PB_CORE}/proto/ -I=${PB_GOGO} -I=${PB_CELESTIA_APP}/proto -I=${PB_NMT} --gogofaster_out=paths=source_relative:. $$file; \ + do protoc -I=. -I=${PB_CORE}/proto/ -I=${PB_NODE} -I=${PB_GOGO} -I=${PB_CELESTIA_APP}/proto -I=${PB_NMT} --gogofaster_out=paths=source_relative:. $$file; \ echo '-->' $$file; \ done; \ done; diff --git a/README.md b/README.md index d4667e6fb0..6db1959599 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Continue reading [here](https://blog.celestia.org/celestia-mvp-release-data-avai - [API docs](#api-docs) - [Node types](#node-types) - [Run a node](#run-a-node) + - [Quick Start with Light Node on arabica](#quick-start-with-light-node-on-arabica) - [Environment variables](#environment-variables) - [Package-specific documentation](#package-specific-documentation) - [Code of Conduct](#code-of-conduct) @@ -31,7 +32,7 @@ Continue reading [here](https://blog.celestia.org/celestia-mvp-release-data-avai ## Minimum requirements | Requirement | Notes | -| ----------- |----------------| +| ----------- | -------------- | | Go version | 1.23 or higher | ## System Requirements @@ -79,6 +80,40 @@ celestia start Please refer to [this guide](https://docs.celestia.org/nodes/celestia-node/) for more information on running a node. +### Quick Start with Light Node on arabica + +View available commands and their usage: + +```sh +make node-help +``` + +Install celestia node and cel-key binaries: + +```sh +make node-install +``` + +Start a light node with automated setup: + +```sh +make light-arabica-up +``` + +This command: + +- Automatically checks wallet balance +- Requests funds from faucet if needed +- Sets node height to latest-1 for quick startup +- Initializes the node if running for the first time + +Options: + +```sh +make light-arabica-up COMMAND=again # Reset node state to latest height +make light-arabica-up CORE_IP= # Use custom core IP +``` + ## Environment variables | Variable | Explanation | Default value | Required | diff --git a/api/docgen/exampledata/txResponse.json b/api/docgen/exampledata/txResponse.json index c03731097f..75fca9f620 100644 --- a/api/docgen/exampledata/txResponse.json +++ b/api/docgen/exampledata/txResponse.json @@ -1,187 +1,5 @@ { "height": 30497, "txhash": "05D9016060072AA71B007A6CFB1B895623192D6616D513017964C3BFCD047282", - "data": "12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365", - "raw_log": "[{\"msg_index\":0,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"celestia12les8l8gzsacjjxwum9wdy7me8x9xajqch4gyw\"},{\"key\":\"amount\",\"value\":\"30utia\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h\"},{\"key\":\"amount\",\"value\":\"30utia\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"celestia12les8l8gzsacjjxwum9wdy7me8x9xajqch4gyw\"},{\"key\":\"sender\",\"value\":\"celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h\"},{\"key\":\"amount\",\"value\":\"30utia\"}]}]}]", - "logs": [ - { - "msg_index": 0, - "events": [ - { - "type": "coin_received", - "attributes": [ - { - "key": "receiver", - "value": "celestia12les8l8gzsacjjxwum9wdy7me8x9xajqch4gyw" - }, - { - "key": "amount", - "value": "30utia" - } - ] - }, - { - "type": "coin_spent", - "attributes": [ - { - "key": "spender", - "value": "celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h" - }, - { - "key": "amount", - "value": "30utia" - } - ] - }, - { - "type": "message", - "attributes": [ - { - "key": "action", - "value": "/cosmos.bank.v1beta1.MsgSend" - }, - { - "key": "sender", - "value": "celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h" - }, - { - "key": "module", - "value": "bank" - } - ] - }, - { - "type": "transfer", - "attributes": [ - { - "key": "recipient", - "value": "celestia12les8l8gzsacjjxwum9wdy7me8x9xajqch4gyw" - }, - { - "key": "sender", - "value": "celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h" - }, - { - "key": "amount", - "value": "30utia" - } - ] - } - ] - } - ], - "gas_wanted": 10000000, - "gas_used": 69085, - "events": [ - { - "type": "tx", - "attributes": [ - { - "key": "ZmVl", - "value": null, - "index": true - } - ] - }, - { - "type": "tx", - "attributes": [ - { - "key": "YWNjX3NlcQ==", - "value": "Y2VsZXN0aWExMzc3azVhbjNmOTR2Nnd5YWNldTBjZjRucTZnazJqdHBjNDZnN2gvMA==", - "index": true - } - ] - }, - { - "type": "tx", - "attributes": [ - { - "key": "c2lnbmF0dXJl", - "value": "R3NlVjhGNThFNGphR05LU0NicDBvNmRILytKK3BNQjNvUmtoNVpKNE8rVjdvNVVYQkJNNXpmNkdiYnN6OW9Takc1OUZkSHJRYzFvVVVBbnRBZW1wV0E9PQ==", - "index": true - } - ] - }, - { - "type": "message", - "attributes": [ - { - "key": "YWN0aW9u", - "value": "L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==", - "index": true - } - ] - }, - { - "type": "coin_spent", - "attributes": [ - { - "key": "c3BlbmRlcg==", - "value": "Y2VsZXN0aWExMzc3azVhbjNmOTR2Nnd5YWNldTBjZjRucTZnazJqdHBjNDZnN2g=", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MzB1dGlh", - "index": true - } - ] - }, - { - "type": "coin_received", - "attributes": [ - { - "key": "cmVjZWl2ZXI=", - "value": "Y2VsZXN0aWExMmxlczhsOGd6c2Fjamp4d3VtOXdkeTdtZTh4OXhhanFjaDRneXc=", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MzB1dGlh", - "index": true - } - ] - }, - { - "type": "transfer", - "attributes": [ - { - "key": "cmVjaXBpZW50", - "value": "Y2VsZXN0aWExMmxlczhsOGd6c2Fjamp4d3VtOXdkeTdtZTh4OXhhanFjaDRneXc=", - "index": true - }, - { - "key": "c2VuZGVy", - "value": "Y2VsZXN0aWExMzc3azVhbjNmOTR2Nnd5YWNldTBjZjRucTZnazJqdHBjNDZnN2g=", - "index": true - }, - { - "key": "YW1vdW50", - "value": "MzB1dGlh", - "index": true - } - ] - }, - { - "type": "message", - "attributes": [ - { - "key": "c2VuZGVy", - "value": "Y2VsZXN0aWExMzc3azVhbjNmOTR2Nnd5YWNldTBjZjRucTZnazJqdHBjNDZnN2g=", - "index": true - } - ] - }, - { - "type": "message", - "attributes": [ - { - "key": "bW9kdWxl", - "value": "YmFuaw==", - "index": true - } - ] - } - ] + "code": 0 } \ No newline at end of file diff --git a/api/docgen/examples.go b/api/docgen/examples.go index f78d29543b..572d5c3f04 100644 --- a/api/docgen/examples.go +++ b/api/docgen/examples.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "reflect" + "time" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -22,6 +23,7 @@ import ( "github.com/celestiaorg/go-fraud" libhead "github.com/celestiaorg/go-header" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/blob" @@ -33,167 +35,158 @@ import ( "github.com/celestiaorg/celestia-node/state" ) -//go:embed "exampledata/extendedHeader.json" -var exampleExtendedHeader string - -//go:embed "exampledata/samplingStats.json" -var exampleSamplingStats string - -//go:embed "exampledata/txResponse.json" -var exampleTxResponse string - -//go:embed "exampledata/resourceManagerStats.json" -var exampleResourceMngrStats string - -//go:embed "exampledata/blob.json" -var exampleBlob string - -//go:embed "exampledata/blobProof.json" -var exampleBlobProof string - -var ExampleValues = map[reflect.Type]interface{}{ - reflect.TypeOf(""): "string value", - reflect.TypeOf(uint64(42)): uint64(42), - reflect.TypeOf(uint32(42)): uint32(42), - reflect.TypeOf(int32(42)): int32(42), - reflect.TypeOf(int64(42)): int64(42), - reflect.TypeOf(42): 42, - reflect.TypeOf(byte(7)): byte(7), - reflect.TypeOf(float64(42)): float64(42), - reflect.TypeOf(true): true, - reflect.TypeOf([]byte{}): []byte("byte array"), - reflect.TypeOf(node.Full): node.Full, - reflect.TypeOf(auth.Permission("admin")): auth.Permission("admin"), - reflect.TypeOf(byzantine.BadEncoding): byzantine.BadEncoding, - reflect.TypeOf((*fraud.Proof[*header.ExtendedHeader])(nil)).Elem(): byzantine.CreateBadEncodingProof( +var ( + //go:embed "exampledata/extendedHeader.json" + exampleExtendedHeader string + + //go:embed "exampledata/samplingStats.json" + exampleSamplingStats string + + //go:embed "exampledata/txResponse.json" + exampleTxResponse string + + //go:embed "exampledata/resourceManagerStats.json" + exampleResourceMngrStats string + + //go:embed "exampledata/blob.json" + exampleBlob string + + //go:embed "exampledata/blobProof.json" + exampleBlobProof string +) + +var exampleValues = map[reflect.Type]any{} + +func add(v any) { + typ := reflect.TypeOf(v) + exampleValues[typ] = v +} + +func init() { + add("string value") + add(uint64(42)) + add(uint32(42)) + add(int32(42)) + add(int64(42)) + add(42) + add(byte(7)) + add(float64(42)) + add(float64(42)) + add(true) + add([]byte("byte array")) + add(time.Second) + add(node.Full) + add(auth.Permission("admin")) + add(byzantine.BadEncoding) + + // TODO: this case requires more debugging, simple to leave it as it was. + exampleValues[reflect.TypeOf((*fraud.Proof[*header.ExtendedHeader])(nil)).Elem()] = byzantine.CreateBadEncodingProof( []byte("bad encoding proof"), 42, &byzantine.ErrByzantine{ Index: 0, - Axis: rsmt2d.Axis(0), Shares: []*byzantine.ShareWithProof{}, + Axis: rsmt2d.Axis(0), }, - ), - reflect.TypeOf((*error)(nil)).Elem(): errors.New("error"), - reflect.TypeOf(state.Balance{}): state.Balance{Amount: sdk.NewInt(42), Denom: "utia"}, -} + ) -func init() { - addToExampleValues(share.EmptyExtendedDataSquare()) - addr, err := sdk.AccAddressFromBech32("celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h") - if err != nil { - panic(err) - } - addToExampleValues(addr) - ExampleValues[reflect.TypeOf((*sdk.Address)(nil)).Elem()] = addr + add(errors.New("error")) + add(state.Balance{Amount: sdk.NewInt(42), Denom: "utia"}) + add(share.EmptyEDS()) + add(rsmt2d.Row) + add(network.Connected) + add(network.ReachabilityPrivate) - valAddr, err := sdk.ValAddressFromBech32("celestiavaloper1q3v5cugc8cdpud87u4zwy0a74uxkk6u4q4gx4p") - if err != nil { - panic(err) - } - addToExampleValues(valAddr) + addr := must(sdk.AccAddressFromBech32("celestia1377k5an3f94v6wyaceu0cf4nq6gk2jtpc46g7h")) + add(addr) + add(state.Address{Address: addr}) + exampleValues[reflect.TypeOf((*sdk.Address)(nil)).Elem()] = addr - addToExampleValues(state.Address{Address: addr}) + valAddr := must(sdk.ValAddressFromBech32("celestiavaloper1q3v5cugc8cdpud87u4zwy0a74uxkk6u4q4gx4p")) + add(valAddr) var txResponse *state.TxResponse - err = json.Unmarshal([]byte(exampleTxResponse), &txResponse) + err := json.Unmarshal([]byte(exampleTxResponse), &txResponse) if err != nil { panic(err) } + add(txResponse) var samplingStats das.SamplingStats err = json.Unmarshal([]byte(exampleSamplingStats), &samplingStats) if err != nil { panic(err) } + add(samplingStats) var extendedHeader *header.ExtendedHeader err = json.Unmarshal([]byte(exampleExtendedHeader), &extendedHeader) if err != nil { panic(err) } + add(extendedHeader) var resourceMngrStats rcmgr.ResourceManagerStat err = json.Unmarshal([]byte(exampleResourceMngrStats), &resourceMngrStats) if err != nil { panic(err) } + add(resourceMngrStats) var exBlob *blob.Blob err = json.Unmarshal([]byte(exampleBlob), &exBlob) if err != nil { panic(err) } + add(exBlob) + add(exBlob.Blob) var blobProof *blob.Proof err = json.Unmarshal([]byte(exampleBlobProof), &blobProof) if err != nil { panic(err) } - - addToExampleValues(exBlob) - addToExampleValues(blobProof) - addToExampleValues(txResponse) - addToExampleValues(samplingStats) - addToExampleValues(extendedHeader) - addToExampleValues(resourceMngrStats) + add(blobProof) mathInt, _ := math.NewIntFromString("42") - addToExampleValues(mathInt) - - addToExampleValues(network.Connected) - addToExampleValues(network.ReachabilityPrivate) + add(mathInt) pID := protocol.ID("/celestia/mocha/ipfs/bitswap") - addToExampleValues(pID) + add(pID) peerID := peer.ID("12D3KooWPRb5h3g9MH7sx9qfbSQZG5cXv1a2Qs3o4aW5YmmzPq82") - addToExampleValues(peerID) + add(peerID) ma, _ := multiaddr.NewMultiaddr("/ip6/::1/udp/2121/quic-v1") addrInfo := peer.AddrInfo{ ID: peerID, Addrs: []multiaddr.Multiaddr{ma}, } - addToExampleValues(addrInfo) + add(addrInfo) - commitment, err := base64.StdEncoding.DecodeString("aHlbp+J9yub6hw/uhK6dP8hBLR2mFy78XNRRdLf2794=") - if err != nil { - panic(err) - } - addToExampleValues(blob.Commitment(commitment)) + commitment := must(base64.StdEncoding.DecodeString("aHlbp+J9yub6hw/uhK6dP8hBLR2mFy78XNRRdLf2794=")) + add(blob.Commitment(commitment)) // randomly generated namespace that's used in the blob example above // (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJ/xGlNMdE=) - namespace, err := share.NewBlobNamespaceV0([]byte{0xc2, 0x7f, 0xc4, 0x69, 0x4d, 0x31, 0xd1}) - if err != nil { - panic(err) - } - addToExampleValues(namespace) + namespace := must(libshare.NewV0Namespace([]byte{0xc2, 0x7f, 0xc4, 0x69, 0x4d, 0x31, 0xd1})) + add(namespace) hashStr := "453D0BC3CB88A2ED6F2E06021383B22C72D25D7741AE51B4CAE1AD34D72A3F07" - hash, err := hex.DecodeString(hashStr) - if err != nil { - panic(err) - } - addToExampleValues(libhead.Hash(hash)) + hash := must(hex.DecodeString(hashStr)) + add(libhead.Hash(hash)) - txConfig := state.NewTxConfig( + add(state.NewTxConfig( state.WithGasPrice(0.002), state.WithGas(142225), state.WithKeyName("my_celes_key"), state.WithSignerAddress("celestia1pjcmwj8w6hyr2c4wehakc5g8cfs36aysgucx66"), state.WithFeeGranterAddress("celestia1hakc56ax66ypjcmwj8w6hyr2c4g8cfs3wesguc"), - ) - addToExampleValues(txConfig) -} - -func addToExampleValues(v interface{}) { - ExampleValues[reflect.TypeOf(v)] = v + )) } -func ExampleValue(t, parent reflect.Type) (interface{}, error) { - v, ok := ExampleValues[t] +func exampleValue(t, parent reflect.Type) (any, error) { + v, ok := exampleValues[t] if ok { return v, nil } @@ -201,26 +194,26 @@ func ExampleValue(t, parent reflect.Type) (interface{}, error) { switch t.Kind() { case reflect.Slice: out := reflect.New(t).Elem() - val, err := ExampleValue(t.Elem(), t) + val, err := exampleValue(t.Elem(), t) if err != nil { return nil, err } out = reflect.Append(out, reflect.ValueOf(val)) return out.Interface(), nil case reflect.Chan: - return ExampleValue(t.Elem(), nil) + return exampleValue(t.Elem(), nil) case reflect.Struct: es, err := exampleStruct(t, parent) if err != nil { return nil, err } v := reflect.ValueOf(es).Elem().Interface() - ExampleValues[t] = v + exampleValues[t] = v return v, nil case reflect.Array: out := reflect.New(t).Elem() for i := 0; i < t.Len(); i++ { - val, err := ExampleValue(t.Elem(), t) + val, err := exampleValue(t.Elem(), t) if err != nil { return nil, err } @@ -242,7 +235,7 @@ func ExampleValue(t, parent reflect.Type) (interface{}, error) { return nil, fmt.Errorf("failed to retrieve example value for type: %s on parent '%s')", t, parent) } -func exampleStruct(t, parent reflect.Type) (interface{}, error) { +func exampleStruct(t, parent reflect.Type) (any, error) { ns := reflect.New(t) for i := 0; i < t.NumField(); i++ { f := t.Field(i) @@ -250,7 +243,7 @@ func exampleStruct(t, parent reflect.Type) (interface{}, error) { continue } if cases.Title(language.Und, cases.NoLower).String(f.Name) == f.Name { - val, err := ExampleValue(f.Type, t) + val, err := exampleValue(f.Type, t) if err != nil { return nil, err } @@ -260,3 +253,10 @@ func exampleStruct(t, parent reflect.Type) (interface{}, error) { return ns.Interface(), nil } + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} diff --git a/api/docgen/openrpc.go b/api/docgen/openrpc.go index a5e52e7ee1..86d901372f 100644 --- a/api/docgen/openrpc.go +++ b/api/docgen/openrpc.go @@ -185,7 +185,7 @@ func NewOpenRPCDocument(comments, permissions Comments) *go_openrpc_reflect.Docu } appReflector.FnSchemaExamples = func(ty reflect.Type) (examples *meta_schema.Examples, err error) { - v, err := ExampleValue(ty, ty) // This isn't ideal, but seems to work well enough. + v, err := exampleValue(ty, ty) // This isn't ideal, but seems to work well enough. if err != nil { fmt.Println(err) } diff --git a/api/gateway/availability.go b/api/gateway/availability.go index e8e96083d5..b399b66483 100644 --- a/api/gateway/availability.go +++ b/api/gateway/availability.go @@ -27,13 +27,7 @@ func (h *Handler) handleHeightAvailabilityRequest(w http.ResponseWriter, r *http return } - header, err := h.header.GetByHeight(r.Context(), uint64(height)) - if err != nil { - writeError(w, http.StatusInternalServerError, heightAvailabilityEndpoint, err) - return - } - - err = h.share.SharesAvailable(r.Context(), header) + err = h.share.SharesAvailable(r.Context(), uint64(height)) switch { case err == nil: resp, err := json.Marshal(&AvailabilityResponse{Available: true}) diff --git a/api/gateway/share.go b/api/gateway/share.go index cdd40e1d2b..835916d923 100644 --- a/api/gateway/share.go +++ b/api/gateway/share.go @@ -9,9 +9,7 @@ import ( "github.com/gorilla/mux" - "github.com/celestiaorg/go-square/shares" - - "github.com/celestiaorg/celestia-node/share" + libshare "github.com/celestiaorg/go-square/v2/share" ) const ( @@ -24,8 +22,8 @@ var namespaceKey = "nid" // NamespacedSharesResponse represents the response to a // SharesByNamespace request. type NamespacedSharesResponse struct { - Shares []share.Share `json:"shares"` - Height uint64 `json:"height"` + Shares []libshare.Share `json:"shares"` + Height uint64 `json:"height"` } // NamespacedDataResponse represents the response to a @@ -90,13 +88,12 @@ func (h *Handler) handleDataByNamespaceRequest(w http.ResponseWriter, r *http.Re } } -func (h *Handler) getShares(ctx context.Context, height uint64, namespace share.Namespace) ([]share.Share, error) { - header, err := h.header.GetByHeight(ctx, height) - if err != nil { - return nil, err - } - - shares, err := h.share.GetSharesByNamespace(ctx, header, namespace) +func (h *Handler) getShares( + ctx context.Context, + height uint64, + namespace libshare.Namespace, +) ([]libshare.Share, error) { + shares, err := h.share.GetNamespaceData(ctx, height, namespace) if err != nil { return nil, err } @@ -104,12 +101,8 @@ func (h *Handler) getShares(ctx context.Context, height uint64, namespace share. return shares.Flatten(), nil } -func dataFromShares(input []share.Share) (data [][]byte, err error) { - appShares, err := shares.FromBytes(input) - if err != nil { - return nil, err - } - sequences, err := shares.ParseShares(appShares, false) +func dataFromShares(input []libshare.Share) (data [][]byte, err error) { + sequences, err := libshare.ParseShares(input, false) if err != nil { return nil, err } @@ -123,19 +116,24 @@ func dataFromShares(input []share.Share) (data [][]byte, err error) { return data, nil } -func parseGetByNamespaceArgs(r *http.Request) (height uint64, namespace share.Namespace, err error) { +func parseGetByNamespaceArgs(r *http.Request) (height uint64, namespace libshare.Namespace, err error) { vars := mux.Vars(r) // if a height was given, parse it, otherwise get namespaced shares/data from the latest header if strHeight, ok := vars[heightKey]; ok { height, err = strconv.ParseUint(strHeight, 10, 64) if err != nil { - return 0, nil, err + return 0, libshare.Namespace{}, err } } hexNamespace := vars[namespaceKey] - namespace, err = hex.DecodeString(hexNamespace) + nsString, err := hex.DecodeString(hexNamespace) + if err != nil { + return 0, libshare.Namespace{}, err + } + ns, err := libshare.NewNamespaceFromBytes(nsString) if err != nil { - return 0, nil, err + return 0, libshare.Namespace{}, err } + namespace = ns return height, namespace, namespace.ValidateForData() } diff --git a/api/gateway/share_test.go b/api/gateway/share_test.go index 1928c721ec..1096287ffc 100644 --- a/api/gateway/share_test.go +++ b/api/gateway/share_test.go @@ -6,11 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/shares" - - "github.com/celestiaorg/celestia-node/share/sharetest" + libshare "github.com/celestiaorg/go-square/v2/share" ) func Test_dataFromShares(t *testing.T) { @@ -20,17 +16,12 @@ func Test_dataFromShares(t *testing.T) { []byte("BEEEEAHP"), } - ns := sharetest.RandV0Namespace() - sss := shares.NewSparseShareSplitter() + ns := libshare.RandomNamespace() + sss := libshare.NewSparseShareSplitter() for _, data := range testData { - b := blob.Blob{ - Data: data, - NamespaceId: ns.ID(), - NamespaceVersion: uint32(ns.Version()), - ShareVersion: uint32(appconsts.ShareVersionZero), - } - err := sss.Write(&b) + b, err := libshare.NewBlob(ns, data, libshare.ShareVersionZero, nil) require.NoError(t, err) + require.NoError(t, sss.Write(b)) } sssShares := sss.Export() @@ -41,7 +32,9 @@ func Test_dataFromShares(t *testing.T) { rawSSSShares[i] = d } - parsedSSSShares, err := dataFromShares(rawSSSShares) + shrs, err := libshare.FromBytes(rawSSSShares) + require.NoError(t, err) + parsedSSSShares, err := dataFromShares(shrs) require.NoError(t, err) require.Equal(t, testData, parsedSSSShares) diff --git a/api/gateway/state.go b/api/gateway/state.go index ee4708f951..92171a9e7e 100644 --- a/api/gateway/state.go +++ b/api/gateway/state.go @@ -17,10 +17,7 @@ const ( const addrKey = "address" -var ( - ErrInvalidAddressFormat = errors.New("address must be a valid account or validator address") - ErrMissingAddress = errors.New("address not specified") -) +var ErrInvalidAddressFormat = errors.New("address must be a valid account or validator address") func (h *Handler) handleBalanceRequest(w http.ResponseWriter, r *http.Request) { var ( diff --git a/api/rpc/perms/permissions.go b/api/rpc/perms/permissions.go index 1bedb1eefc..8a72e21676 100644 --- a/api/rpc/perms/permissions.go +++ b/api/rpc/perms/permissions.go @@ -1,7 +1,9 @@ package perms import ( + "crypto/rand" "encoding/json" + "time" "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" @@ -19,7 +21,9 @@ var AuthKey = "Authorization" // JWTPayload is a utility struct for marshaling/unmarshalling // permissions into for token signing/verifying. type JWTPayload struct { - Allow []auth.Permission + Allow []auth.Permission + Nonce []byte + ExpiresAt time.Time } func (j *JWTPayload) MarshalBinary() (data []byte, err error) { @@ -29,8 +33,26 @@ func (j *JWTPayload) MarshalBinary() (data []byte, err error) { // NewTokenWithPerms generates and signs a new JWT token with the given secret // and given permissions. func NewTokenWithPerms(signer jwt.Signer, perms []auth.Permission) ([]byte, error) { + return NewTokenWithTTL(signer, perms, 0) +} + +// NewTokenWithTTL generates and signs a new JWT token with the given secret +// and given permissions and TTL. +func NewTokenWithTTL(signer jwt.Signer, perms []auth.Permission, ttl time.Duration) ([]byte, error) { + nonce := make([]byte, 32) + if _, err := rand.Read(nonce); err != nil { + return nil, err + } + + var expiresAt time.Time + if ttl != 0 { + expiresAt = time.Now().UTC().Add(ttl) + } + p := &JWTPayload{ - Allow: perms, + Allow: perms, + Nonce: nonce, + ExpiresAt: expiresAt, } token, err := jwt.NewBuilder(signer).Build(p) if err != nil { diff --git a/api/rpc_test.go b/api/rpc_test.go index db65d2a5f8..343514632a 100644 --- a/api/rpc_test.go +++ b/api/rpc_test.go @@ -89,6 +89,42 @@ func TestRPCCallsUnderlyingNode(t *testing.T) { require.Equal(t, expectedBalance, balance) } +func TestRPCCallsTokenExpired(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + // generate dummy signer and sign admin perms token with it + key := make([]byte, 32) + + signer, err := jwt.NewSignerHS(jwt.HS256, key) + require.NoError(t, err) + + verifier, err := jwt.NewVerifierHS(jwt.HS256, key) + require.NoError(t, err) + + nd, _ := setupNodeWithAuthedRPC(t, signer, verifier) + url := nd.RPCServer.ListenAddr() + + adminToken, err := perms.NewTokenWithTTL(signer, perms.AllPerms, time.Millisecond) + require.NoError(t, err) + + // we need to run this a few times to prevent the race where the server is not yet started + var rpcClient *client.Client + for i := 0; i < 3; i++ { + time.Sleep(time.Second * 1) + rpcClient, err = client.NewClient(ctx, "http://"+url, string(adminToken)) + if err == nil { + t.Cleanup(rpcClient.Close) + break + } + } + require.NotNil(t, rpcClient) + require.NoError(t, err) + + _, err = rpcClient.State.Balance(ctx) + require.ErrorContains(t, err, "request failed, http status 401 Unauthorized") +} + // api contains all modules that are made available as the node's // public API surface type api struct { diff --git a/blob/blob.go b/blob/blob.go index 369d464ef4..4bdc3d1045 100644 --- a/blob/blob.go +++ b/blob/blob.go @@ -6,23 +6,20 @@ import ( "errors" "fmt" - "github.com/tendermint/tendermint/crypto/merkle" - - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - v2 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v2" - "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/inclusion" - "github.com/celestiaorg/go-square/shares" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/go-square/merkle" + "github.com/celestiaorg/go-square/v2/inclusion" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" - - "github.com/celestiaorg/celestia-node/share" ) // appVersion is the current application version of celestia-app. -const appVersion = v2.Version +const appVersion = appconsts.LatestVersion var errEmptyShares = errors.New("empty shares") +var subtreeRootThreshold = appconsts.SubtreeRootThreshold(appVersion) + // The Proof is a set of nmt proofs that can be verified only through // the included method (due to limitation of the nmt https://github.com/celestiaorg/nmt/issues/218). // Proof proves the WHOLE namespaced data to the row roots. @@ -61,14 +58,10 @@ func (p Proof) equal(input Proof) error { // Blob represents any application-specific binary data that anyone can submit to Celestia. type Blob struct { - *blob.Blob `json:"blob"` + *libshare.Blob `json:"blob"` Commitment Commitment `json:"commitment"` - // the celestia-node's namespace type - // this is to avoid converting to and from app's type - namespace share.Namespace - // index represents the index of the blob's first share in the EDS. // Only retrieved, on-chain blobs will have the index set. Default is -1. index int @@ -76,36 +69,37 @@ type Blob struct { // NewBlobV0 constructs a new blob from the provided Namespace and data. // The blob will be formatted as v0 shares. -func NewBlobV0(namespace share.Namespace, data []byte) (*Blob, error) { - return NewBlob(appconsts.ShareVersionZero, namespace, data) +func NewBlobV0(namespace libshare.Namespace, data []byte) (*Blob, error) { + return NewBlob(libshare.ShareVersionZero, namespace, data, nil) } -// NewBlob constructs a new blob from the provided Namespace, data and share version. -func NewBlob(shareVersion uint8, namespace share.Namespace, data []byte) (*Blob, error) { - if len(data) == 0 || len(data) > appconsts.DefaultMaxBytes { - return nil, fmt.Errorf("blob data must be > 0 && <= %d, but it was %d bytes", appconsts.DefaultMaxBytes, len(data)) - } +// NewBlobV1 constructs a new blob from the provided Namespace, data, and signer. +// The blob will be formatted as v1 shares. +func NewBlobV1(namespace libshare.Namespace, data, signer []byte) (*Blob, error) { + return NewBlob(libshare.ShareVersionOne, namespace, data, signer) +} + +// NewBlob constructs a new blob from the provided Namespace, data, signer, and share version. +func NewBlob(shareVersion uint8, namespace libshare.Namespace, data, signer []byte) (*Blob, error) { if err := namespace.ValidateForBlob(); err != nil { - return nil, err + return nil, fmt.Errorf("invalid user namespace: %w", err) } - blob := blob.Blob{ - NamespaceId: namespace.ID(), - Data: data, - ShareVersion: uint32(shareVersion), - NamespaceVersion: uint32(namespace.Version()), + libBlob, err := libshare.NewBlob(namespace, data, shareVersion, signer) + if err != nil { + return nil, err } - com, err := inclusion.CreateCommitment(&blob, merkle.HashFromByteSlices, appconsts.SubtreeRootThreshold(appVersion)) + com, err := inclusion.CreateCommitment(libBlob, merkle.HashFromByteSlices, subtreeRootThreshold) if err != nil { return nil, err } - return &Blob{Blob: &blob, Commitment: com, namespace: namespace, index: -1}, nil + return &Blob{Blob: libBlob, Commitment: com, index: -1}, nil } // Namespace returns blob's namespace. -func (b *Blob) Namespace() share.Namespace { - return b.namespace +func (b *Blob) Namespace() libshare.Namespace { + return b.Blob.Namespace() } // Index returns the blob's first share index in the EDS. @@ -124,18 +118,12 @@ func (b *Blob) Length() (int, error) { if len(s) == 0 { return 0, errors.New("blob with zero shares received") } + return libshare.SparseSharesNeeded(s[0].SequenceLen()), nil +} - appShare, err := shares.NewShare(s[0]) - if err != nil { - return 0, err - } - - seqLength, err := appShare.SequenceLen() - if err != nil { - return 0, err - } - - return shares.SparseSharesNeeded(seqLength), nil +// Signer returns blob's author. +func (b *Blob) Signer() []byte { + return b.Blob.Signer() } func (b *Blob) compareCommitments(com Commitment) bool { @@ -143,19 +131,21 @@ func (b *Blob) compareCommitments(com Commitment) bool { } type jsonBlob struct { - Namespace share.Namespace `json:"namespace"` - Data []byte `json:"data"` - ShareVersion uint32 `json:"share_version"` - Commitment Commitment `json:"commitment"` - Index int `json:"index"` + Namespace []byte `json:"namespace"` + Data []byte `json:"data"` + ShareVersion uint8 `json:"share_version"` + Commitment Commitment `json:"commitment"` + Signer []byte `json:"signer,omitempty"` + Index int `json:"index"` } func (b *Blob) MarshalJSON() ([]byte, error) { blob := &jsonBlob{ - Namespace: b.Namespace(), - Data: b.Data, - ShareVersion: b.ShareVersion, + Namespace: b.Namespace().Bytes(), + Data: b.Data(), + ShareVersion: b.ShareVersion(), Commitment: b.Commitment, + Signer: b.Signer(), Index: b.index, } return json.Marshal(blob) @@ -168,13 +158,18 @@ func (b *Blob) UnmarshalJSON(data []byte) error { return err } - b.Blob = &blob.Blob{} - b.Blob.NamespaceVersion = uint32(jsonBlob.Namespace.Version()) - b.Blob.NamespaceId = jsonBlob.Namespace.ID() - b.Blob.Data = jsonBlob.Data - b.Blob.ShareVersion = jsonBlob.ShareVersion - b.Commitment = jsonBlob.Commitment - b.namespace = jsonBlob.Namespace - b.index = jsonBlob.Index + ns, err := libshare.NewNamespaceFromBytes(jsonBlob.Namespace) + if err != nil { + return err + } + + blob, err := NewBlob(jsonBlob.ShareVersion, ns, jsonBlob.Data, jsonBlob.Signer) + if err != nil { + return err + } + + blob.Commitment = jsonBlob.Commitment + blob.index = jsonBlob.Index + *b = *blob return nil } diff --git a/blob/blob_fuzz_test.go b/blob/blob_fuzz_test.go new file mode 100644 index 0000000000..1927ff3d46 --- /dev/null +++ b/blob/blob_fuzz_test.go @@ -0,0 +1,92 @@ +package blob + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +func FuzzProofEqual(f *testing.F) { + if testing.Short() { + f.Skip("in -short mode") + } + + // 1. Generate the corpus. + libBlobs, err := libshare.GenerateV0Blobs([]int{16}, false) + if err != nil { + f.Fatal(err) + } + + blobs, err := convertBlobs(libBlobs...) + if err != nil { + f.Fatal(err) + } + + for _, blob := range blobs { + jsonBlob, err := blob.MarshalJSON() + if err != nil { + f.Fatal(err) + } + f.Add(jsonBlob) + } + + // 2. Run the fuzzer. + f.Fuzz(func(t *testing.T, jsonBlob []byte) { + blob := new(Blob) + _ = blob.UnmarshalJSON(jsonBlob) + }) +} + +type verifyCorpus struct { + CP *CommitmentProof `json:"commitment_proof"` + Root []byte `json:"root"` + SThreshold int `json:"sub_threshold"` +} + +func FuzzCommitmentProofVerify(f *testing.F) { + if testing.Short() { + f.Skip("in -short mode") + } + + path := filepath.Join("testdata", "fuzz-corpus", "verify-*.json") + paths, err := filepath.Glob(path) + if err != nil { + f.Fatal(err) + } + + // Add the corpra. + for _, path := range paths { + blob, err := os.ReadFile(path) + if err != nil { + f.Fatal(err) + } + + var values []*verifyCorpus + if err := json.Unmarshal(blob, &values); err != nil { + f.Fatal(err) + } + + for _, value := range values { + blob, err := json.Marshal(value) + if err != nil { + f.Fatal(err) + } + f.Add(blob) + } + } + + f.Fuzz(func(t *testing.T, valueJSON []byte) { + val := new(verifyCorpus) + if err := json.Unmarshal(valueJSON, val); err != nil { + return + } + commitProof := val.CP + if commitProof == nil { + return + } + _, _ = commitProof.Verify(val.Root, val.SThreshold) + }) +} diff --git a/blob/blob_test.go b/blob/blob_test.go index d747090758..221e2d8395 100644 --- a/blob/blob_test.go +++ b/blob/blob_test.go @@ -7,20 +7,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - apptypes "github.com/celestiaorg/celestia-app/v2/x/blob/types" - "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/inclusion" "github.com/celestiaorg/go-square/merkle" - - "github.com/celestiaorg/celestia-node/blob/blobtest" + "github.com/celestiaorg/go-square/v2/inclusion" + libshare "github.com/celestiaorg/go-square/v2/share" ) func TestBlob(t *testing.T) { length := 16 - appBlobs, err := blobtest.GenerateV0Blobs([]int{length}, false) + libBlobs, err := libshare.GenerateV0Blobs([]int{length}, false) require.NoError(t, err) - blob, err := convertBlobs(appBlobs...) + blob, err := convertBlobs(libBlobs...) require.NoError(t, err) test := []struct { @@ -42,7 +38,7 @@ func TestBlob(t *testing.T) { comm, err := inclusion.CreateCommitment( blob[0].Blob, merkle.HashFromByteSlices, - appconsts.SubtreeRootThreshold(appVersion), + subtreeRootThreshold, ) require.NoError(t, err) assert.Equal(t, blob[0].Commitment, Commitment(comm)) @@ -51,9 +47,8 @@ func TestBlob(t *testing.T) { { name: "verify namespace", expectedRes: func(t *testing.T) { - ns := blob[0].Namespace().ToAppNamespace() - require.NoError(t, err) - require.NoError(t, apptypes.ValidateBlobNamespace(ns)) + ns := blob[0].Namespace() + require.NoError(t, ns.ValidateForBlob()) }, }, { @@ -67,9 +62,7 @@ func TestBlob(t *testing.T) { { name: "shares to blobs", expectedRes: func(t *testing.T) { - sh, err := BlobsToShares(blob...) - require.NoError(t, err) - shares, err := toAppShares(sh...) + shares, err := BlobsToShares(blob...) require.NoError(t, err) p := &parser{length: len(shares), shares: shares} b, err := p.parse() @@ -85,7 +78,7 @@ func TestBlob(t *testing.T) { newBlob := &Blob{} require.NoError(t, newBlob.UnmarshalJSON(data)) - require.True(t, bytes.Equal(blob[0].Blob.GetData(), newBlob.Data)) + require.True(t, bytes.Equal(blob[0].Blob.Data(), newBlob.Data())) require.True(t, bytes.Equal(blob[0].Commitment, newBlob.Commitment)) }, }, @@ -96,10 +89,10 @@ func TestBlob(t *testing.T) { } } -func convertBlobs(appBlobs ...*blob.Blob) ([]*Blob, error) { - blobs := make([]*Blob, 0, len(appBlobs)) - for _, appBlob := range appBlobs { - blob, err := NewBlob(uint8(appBlob.GetShareVersion()), appBlob.Namespace().Bytes(), appBlob.GetData()) +func convertBlobs(libBlobs ...*libshare.Blob) ([]*Blob, error) { + blobs := make([]*Blob, 0, len(libBlobs)) + for _, libBlob := range libBlobs { + blob, err := NewBlob(libBlob.ShareVersion(), libBlob.Namespace(), libBlob.Data(), libBlob.Signer()) if err != nil { return nil, err } diff --git a/blob/blobtest/testing.go b/blob/blobtest/testing.go deleted file mode 100644 index eb85bead27..0000000000 --- a/blob/blobtest/testing.go +++ /dev/null @@ -1,38 +0,0 @@ -package blobtest - -import ( - tmrand "github.com/tendermint/tendermint/libs/rand" - - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/test/util/testfactory" - "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/shares" - - "github.com/celestiaorg/celestia-node/share" -) - -// GenerateV0Blobs is a test utility producing v0 share formatted blobs with the -// requested size and random namespaces. -func GenerateV0Blobs(sizes []int, sameNamespace bool) ([]*blob.Blob, error) { - blobs := make([]*blob.Blob, 0, len(sizes)) - - for _, size := range sizes { - size := rawBlobSize(appconsts.FirstSparseShareContentSize * size) - appBlob := testfactory.GenerateRandomBlob(size) - if !sameNamespace { - namespace, err := share.NewBlobNamespaceV0(tmrand.Bytes(7)) - if err != nil { - return nil, err - } - appBlob.NamespaceVersion = uint32(namespace[0]) - appBlob.NamespaceId = namespace[1:] - } - - blobs = append(blobs, appBlob) - } - return blobs, nil -} - -func rawBlobSize(totalSize int) int { - return totalSize - shares.DelimLen(uint64(totalSize)) -} diff --git a/blob/commitment_proof.go b/blob/commitment_proof.go index 8fa7467179..887f420552 100644 --- a/blob/commitment_proof.go +++ b/blob/commitment_proof.go @@ -2,15 +2,15 @@ package blob import ( "bytes" + "errors" "fmt" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/pkg/proof" - "github.com/celestiaorg/go-square/inclusion" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/proof" + "github.com/celestiaorg/go-square/v2/inclusion" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/nmt/namespace" - - "github.com/celestiaorg/celestia-node/share" ) // Commitment is a Merkle Root of the subtree built from shares of the Blob. @@ -85,7 +85,16 @@ func (commitmentProof *CommitmentProof) Validate() error { // Expects the commitment proof to be properly formulated and validated // using the Validate() function. func (commitmentProof *CommitmentProof) Verify(root []byte, subtreeRootThreshold int) (bool, error) { - nmtHasher := nmt.NewNmtHasher(appconsts.NewBaseHashFunc(), share.NamespaceSize, true) + if len(root) == 0 { + return false, errors.New("root must be non-empty") + } + + rp := commitmentProof.RowProof + if err := rp.Validate(root); err != nil { + return false, err + } + + nmtHasher := nmt.NewNmtHasher(appconsts.NewBaseHashFunc(), libshare.NamespaceSize, true) // computes the total number of shares proven. numberOfShares := 0 @@ -93,6 +102,10 @@ func (commitmentProof *CommitmentProof) Verify(root []byte, subtreeRootThreshold numberOfShares += proof.End() - proof.Start() } + if subtreeRootThreshold <= 0 { + return false, errors.New("subtreeRootThreshould must be > 0") + } + // use the computed total number of shares to calculate the subtree roots // width. // the subtree roots width is defined in ADR-013: @@ -108,6 +121,15 @@ func (commitmentProof *CommitmentProof) Verify(root []byte, subtreeRootThreshold if err != nil { return false, err } + + if len(commitmentProof.SubtreeRoots) < subtreeRootsCursor { + return false, fmt.Errorf("len(commitmentProof.SubtreeRoots)=%d < subtreeRootsCursor=%d", + len(commitmentProof.SubtreeRoots), subtreeRootsCursor) + } + if len(commitmentProof.SubtreeRoots) < subtreeRootsCursor+len(ranges) { + return false, fmt.Errorf("len(commitmentProof.SubtreeRoots)=%d < subtreeRootsCursor+len(ranges)=%d", + len(commitmentProof.SubtreeRoots), subtreeRootsCursor+len(ranges)) + } valid, err := subtreeRootProof.VerifySubtreeRootInclusion( nmtHasher, commitmentProof.SubtreeRoots[subtreeRootsCursor:subtreeRootsCursor+len(ranges)], diff --git a/blob/helper.go b/blob/helper.go index 17f0068c9e..d764e827e9 100644 --- a/blob/helper.go +++ b/blob/helper.go @@ -3,56 +3,33 @@ package blob import ( "sort" - squareblob "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/shares" - - "github.com/celestiaorg/celestia-node/share" + libshare "github.com/celestiaorg/go-square/v2/share" ) // BlobsToShares accepts blobs and convert them to the Shares. -func BlobsToShares(nodeBlobs ...*Blob) ([]share.Share, error) { - b := make([]*squareblob.Blob, len(nodeBlobs)) - for i, nodeBlob := range nodeBlobs { - namespace := nodeBlob.Namespace() - b[i] = &squareblob.Blob{ - NamespaceVersion: uint32(namespace[0]), - NamespaceId: namespace[1:], - Data: nodeBlob.Data, - ShareVersion: nodeBlob.ShareVersion, - } - } - - sort.Slice(b, func(i, j int) bool { - return b[i].Namespace().Compare(b[j].Namespace()) < 0 +func BlobsToShares(nodeBlobs ...*Blob) ([]libshare.Share, error) { + sort.Slice(nodeBlobs, func(i, j int) bool { + return nodeBlobs[i].Blob.Namespace().IsLessThan(nodeBlobs[j].Blob.Namespace()) }) - rawShares, err := shares.SplitBlobs(b...) - if err != nil { - return nil, err + shares := make([]libshare.Share, 0) + for _, nodeBlob := range nodeBlobs { + sh, err := nodeBlob.ToShares() + if err != nil { + return nil, err + } + shares = append(shares, sh...) } - return shares.ToBytes(rawShares), nil + return shares, nil } -// ToAppBlobs converts node's blob type to the blob type from go-square. -func ToAppBlobs(blobs ...*Blob) []*squareblob.Blob { - appBlobs := make([]*squareblob.Blob, len(blobs)) +// ToLibBlobs converts node's blob type to the blob type from go-square. +func ToLibBlobs(blobs ...*Blob) []*libshare.Blob { + libBlobs := make([]*libshare.Blob, len(blobs)) for i := range blobs { - appBlobs[i] = blobs[i].Blob - } - return appBlobs -} - -// toAppShares converts node's raw shares to the app shares, skipping padding -func toAppShares(shrs ...share.Share) ([]shares.Share, error) { - appShrs := make([]shares.Share, 0, len(shrs)) - for _, shr := range shrs { - bShare, err := shares.NewShare(shr) - if err != nil { - return nil, err - } - appShrs = append(appShrs, *bShare) + libBlobs[i] = blobs[i].Blob } - return appShrs, nil + return libBlobs } func calculateIndex(rowLength, blobIndex int) (row, col int) { diff --git a/blob/helper_test.go b/blob/helper_test.go index 39f0ba7c3f..7af9336fae 100644 --- a/blob/helper_test.go +++ b/blob/helper_test.go @@ -7,52 +7,37 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - squareblob "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/namespace" + libshare "github.com/celestiaorg/go-square/v2/share" ) func TestBlobsToShares(t *testing.T) { t.Run("should sort blobs by namespace in ascending order", func(t *testing.T) { - namespaceA := namespace.MustNewV0(bytes.Repeat([]byte{0xa}, 10)).Bytes() - namespaceB := namespace.MustNewV0(bytes.Repeat([]byte{0xb}, 10)).Bytes() + namespaceA := libshare.MustNewV0Namespace(bytes.Repeat([]byte{0xa}, 10)) + namespaceB := libshare.MustNewV0Namespace(bytes.Repeat([]byte{0xb}, 10)) - blobA, err := NewBlob(appconsts.ShareVersionZero, namespaceA, []byte("dataA")) + blobA, err := NewBlob(libshare.ShareVersionZero, namespaceA, []byte("dataA"), nil) require.NoError(t, err) - blobB, err := NewBlob(appconsts.ShareVersionZero, namespaceB, []byte("dataB")) + blobB, err := NewBlob(libshare.ShareVersionZero, namespaceB, []byte("dataB"), nil) require.NoError(t, err) got, err := BlobsToShares(blobB, blobA) require.NoError(t, err) - assert.Equal(t, got[0][:appconsts.NamespaceSize], namespaceA) - assert.Equal(t, got[1][:appconsts.NamespaceSize], namespaceB) - assert.IsIncreasing(t, got) + assert.Equal(t, got[0].Namespace(), namespaceA) + assert.Equal(t, got[1].Namespace(), namespaceB) + assert.True(t, got[0].Namespace().IsLessThan(got[1].Namespace())) }) } -func TestToAppBlobs(t *testing.T) { - namespaceA := namespace.MustNewV0(bytes.Repeat([]byte{0xa}, 10)) - namespaceB := namespace.MustNewV0(bytes.Repeat([]byte{0xb}, 10)) +func TestToLibBlobs(t *testing.T) { + namespaceA := libshare.MustNewV0Namespace(bytes.Repeat([]byte{0xa}, 10)) + namespaceB := libshare.MustNewV0Namespace(bytes.Repeat([]byte{0xb}, 10)) - blobA, err := NewBlob(appconsts.ShareVersionZero, namespaceA.Bytes(), []byte("dataA")) + blobA, err := NewBlob(libshare.ShareVersionZero, namespaceA, []byte("dataA"), nil) require.NoError(t, err) - blobB, err := NewBlob(appconsts.ShareVersionZero, namespaceB.Bytes(), []byte("dataB")) + blobB, err := NewBlob(libshare.ShareVersionZero, namespaceB, []byte("dataB"), nil) require.NoError(t, err) - got := ToAppBlobs(blobA, blobB) - want := []*squareblob.Blob{ - { - NamespaceId: blobA.NamespaceId, - NamespaceVersion: blobA.NamespaceVersion, - Data: blobA.Data, - ShareVersion: blobA.ShareVersion, - }, - { - NamespaceId: blobB.NamespaceId, - NamespaceVersion: blobB.NamespaceVersion, - Data: blobB.Data, - ShareVersion: blobB.ShareVersion, - }, - } - assert.Equal(t, want, got) + got := ToLibBlobs(blobA, blobB) + + assert.Equal(t, []*libshare.Blob{blobA.Blob, blobB.Blob}, got) } diff --git a/blob/parser.go b/blob/parser.go index 0033d29ebf..977bb2dfd9 100644 --- a/blob/parser.go +++ b/blob/parser.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" - "github.com/celestiaorg/go-square/shares" + "github.com/celestiaorg/go-square/merkle" + "github.com/celestiaorg/go-square/v2/inclusion" + libshare "github.com/celestiaorg/go-square/v2/share" ) // parser helps to collect shares and transform them into a blob. @@ -15,13 +17,13 @@ type parser struct { // length is an amount of the shares needed to build the blob. length int // shares is a set of shares to build the blob. - shares []shares.Share + shares []libshare.Share verifyFn func(blob *Blob) bool } // set tries to find the first blob's share by skipping padding shares and // sets the metadata of the blob(index and length) -func (p *parser) set(index int, shrs []shares.Share) ([]shares.Share, error) { +func (p *parser) set(index int, shrs []libshare.Share) ([]libshare.Share, error) { if len(shrs) == 0 { return nil, errEmptyShares } @@ -37,19 +39,15 @@ func (p *parser) set(index int, shrs []shares.Share) ([]shares.Share, error) { // `+=` as index could be updated in `skipPadding` p.index += index - length, err := shrs[0].SequenceLen() - if err != nil { - return nil, err - } - - p.length = shares.SparseSharesNeeded(length) + length := shrs[0].SequenceLen() + p.length = libshare.SparseSharesNeeded(length) return shrs, nil } // addShares sets shares until the blob is completed and extra remaining shares back. // It assumes that the remaining shares required for blob completeness are correct and // do not include padding shares. -func (p *parser) addShares(shares []shares.Share) (shrs []shares.Share, isComplete bool) { +func (p *parser) addShares(shares []libshare.Share) (shrs []libshare.Share, isComplete bool) { index := -1 for i, sh := range shares { p.shares = append(p.shares, sh) @@ -77,54 +75,34 @@ func (p *parser) parse() (*Blob, error) { return nil, fmt.Errorf("invalid shares amount. want:%d, have:%d", p.length, len(p.shares)) } - sequence, err := shares.ParseShares(p.shares, true) + blobs, err := libshare.ParseBlobs(p.shares) if err != nil { return nil, err } - // ensure that sequence length is not 0 - if len(sequence) == 0 { - return nil, ErrBlobNotFound - } - if len(sequence) > 1 { - return nil, errors.New("unexpected amount of sequences") - } - - data, err := sequence[0].RawData() - if err != nil { - return nil, err - } - if len(data) == 0 { - return nil, ErrBlobNotFound + if len(blobs) != 1 { + return nil, errors.New("unexpected amount of blobs during parsing") } - shareVersion, err := sequence[0].Shares[0].Version() + com, err := inclusion.CreateCommitment(blobs[0], merkle.HashFromByteSlices, subtreeRootThreshold) if err != nil { return nil, err } - blob, err := NewBlob(shareVersion, sequence[0].Namespace.Bytes(), data) - if err != nil { - return nil, err - } - blob.index = p.index + blob := &Blob{Blob: blobs[0], Commitment: com, index: p.index} return blob, nil } // skipPadding iterates through the shares until non-padding share will be found. It guarantees that // the returned set of shares will start with non-padding share(or empty set of shares). -func (p *parser) skipPadding(shares []shares.Share) ([]shares.Share, error) { +func (p *parser) skipPadding(shares []libshare.Share) ([]libshare.Share, error) { if len(shares) == 0 { return nil, errEmptyShares } offset := 0 for _, sh := range shares { - isPadding, err := sh.IsPadding() - if err != nil { - return nil, err - } - if !isPadding { + if !sh.IsPadding() { break } offset++ diff --git a/blob/repro_test.go b/blob/repro_test.go new file mode 100644 index 0000000000..6cb39948ac --- /dev/null +++ b/blob/repro_test.go @@ -0,0 +1,63 @@ +package blob + +import ( + "testing" + + "github.com/celestiaorg/celestia-app/v3/pkg/proof" + "github.com/celestiaorg/nmt" + "github.com/celestiaorg/nmt/pb" +) + +// Reported at https://github.com/celestiaorg/celestia-node/issues/3731. +func TestCommitmentProofRowProofVerifyWithEmptyRoot(t *testing.T) { + cp := &CommitmentProof{ + RowProof: proof.RowProof{ + Proofs: []*proof.Proof{{}}, + }, + } + root := []byte{0xd3, 0x4d, 0x34} + if _, err := cp.Verify(root, 1); err == nil { + t.Fatal("expected a non-nil error") + } +} + +// Reported at https://github.com/celestiaorg/celestia-node/issues/3730. +func TestCommitmentProofRowProofVerify(t *testing.T) { + cp := &CommitmentProof{ + RowProof: proof.RowProof{ + Proofs: []*proof.Proof{{}}, + }, + } + if _, err := cp.Verify(nil, 1); err == nil { + t.Fatal("expected a non-nil error") + } +} + +// Reported at https://github.com/celestiaorg/celestia-node/issues/3729. +func TestCommitmentProofVerifySliceBound(t *testing.T) { + proof := nmt.ProtoToProof(pb.Proof{End: 1}) + cp := &CommitmentProof{ + SubtreeRootProofs: []*nmt.Proof{ + &proof, + }, + } + if _, err := cp.Verify(nil, 1); err == nil { + t.Fatal("expected a non-nil error") + } +} + +// Reported at https://github.com/celestiaorg/celestia-node/issues/3728. +func TestCommitmentProofVerifyZeroSubThreshold(t *testing.T) { + cp := new(CommitmentProof) + if _, err := cp.Verify(nil, 0); err == nil { + t.Fatal("expected a non-nil error") + } +} + +// Reported at https://github.com/celestiaorg/celestia-node/issues/3727. +func TestBlobUnmarshalRepro(t *testing.T) { + blob := new(Blob) + if err := blob.UnmarshalJSON([]byte("{}")); err == nil { + t.Fatal("expected a non-nil error") + } +} diff --git a/blob/service.go b/blob/service.go index ee3b7e5cac..11449da2b5 100644 --- a/blob/service.go +++ b/blob/service.go @@ -1,7 +1,7 @@ package blob import ( - bytes2 "bytes" + "bytes" "context" "encoding/hex" "errors" @@ -16,17 +16,17 @@ import ( "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - pkgproof "github.com/celestiaorg/celestia-app/v2/pkg/proof" - "github.com/celestiaorg/go-square/inclusion" - appns "github.com/celestiaorg/go-square/namespace" - "github.com/celestiaorg/go-square/shares" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + pkgproof "github.com/celestiaorg/celestia-app/v3/pkg/proof" + "github.com/celestiaorg/go-square/v2/inclusion" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/libs/utils" "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" "github.com/celestiaorg/celestia-node/state" ) @@ -46,7 +46,7 @@ type SubmitOptions = state.TxConfig // avoid a circular dependency between the blob and the state package, since the state package needs // the blob.Blob type for this signature. type Submitter interface { - SubmitPayForBlob(context.Context, []*state.Blob, *state.TxConfig) (*types.TxResponse, error) + SubmitPayForBlob(context.Context, []*libshare.Blob, *state.TxConfig) (*types.TxResponse, error) } type Service struct { @@ -56,7 +56,7 @@ type Service struct { // accessor dials the given celestia-core endpoint to submit blobs. blobSubmitter Submitter // shareGetter retrieves the EDS to fetch all shares from the requested header. - shareGetter share.Getter + shareGetter shwap.Getter // headerGetter fetches header by the provided height headerGetter func(context.Context, uint64) (*header.ExtendedHeader, error) // headerSub subscribes to new headers to supply to blob subscriptions. @@ -65,7 +65,7 @@ type Service struct { func NewService( submitter Submitter, - getter share.Getter, + getter shwap.Getter, headerGetter func(context.Context, uint64) (*header.ExtendedHeader, error), headerSub func(ctx context.Context) (<-chan *header.ExtendedHeader, error), ) *Service { @@ -99,7 +99,7 @@ type SubscriptionResponse struct { // The channel will be closed when the context is canceled or the service is stopped. // Please note that no errors are returned: underlying operations are retried until successful. // Additionally, not reading from the returned channel will cause the stream to close after 16 messages. -func (s *Service) Subscribe(ctx context.Context, ns share.Namespace) (<-chan *SubscriptionResponse, error) { +func (s *Service) Subscribe(ctx context.Context, ns libshare.Namespace) (<-chan *SubscriptionResponse, error) { if s.ctx == nil { return nil, fmt.Errorf("service has not been started") } @@ -133,7 +133,7 @@ func (s *Service) Subscribe(ctx context.Context, ns share.Namespace) (<-chan *Su var blobs []*Blob var err error for { - blobs, err = s.getAll(ctx, header, []share.Namespace{ns}) + blobs, err = s.getAll(ctx, header, []libshare.Namespace{ns}) if ctx.Err() != nil { // context canceled, continuing would lead to unexpected missed heights for the client log.Debugw("blobsub: canceling subscription due to user ctx closing", "namespace", ns.ID()) @@ -170,15 +170,16 @@ func (s *Service) Subscribe(ctx context.Context, ns share.Namespace) (<-chan *Su func (s *Service) Submit(ctx context.Context, blobs []*Blob, txConfig *SubmitOptions) (uint64, error) { log.Debugw("submitting blobs", "amount", len(blobs)) - squareBlobs := make([]*state.Blob, len(blobs)) + libBlobs := make([]*libshare.Blob, len(blobs)) for i := range blobs { if err := blobs[i].Namespace().ValidateForBlob(); err != nil { - return 0, err + return 0, fmt.Errorf("not allowed namespace %s were used to build the blob", blobs[i].Namespace().ID()) } - squareBlobs[i] = blobs[i].Blob + + libBlobs[i] = blobs[i].Blob } - resp, err := s.blobSubmitter.SubmitPayForBlob(ctx, squareBlobs, txConfig) + resp, err := s.blobSubmitter.SubmitPayForBlob(ctx, libBlobs, txConfig) if err != nil { return 0, err } @@ -192,7 +193,7 @@ func (s *Service) Submit(ctx context.Context, blobs []*Blob, txConfig *SubmitOpt func (s *Service) Get( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, commitment Commitment, ) (blob *Blob, err error) { ctx, span := tracer.Start(ctx, "get") @@ -218,7 +219,7 @@ func (s *Service) Get( func (s *Service) GetProof( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, commitment Commitment, ) (proof *Proof, err error) { ctx, span := tracer.Start(ctx, "get-proof") @@ -235,7 +236,7 @@ func (s *Service) GetProof( }} _, proof, err = s.retrieve(ctx, height, namespace, sharesParser) - return proof, nil + return proof, err } // GetAll returns all blobs under the given namespaces at the given height. @@ -247,7 +248,7 @@ func (s *Service) GetProof( // the user will receive all found blobs along with a combined error message. // // All blobs will preserve the order of the namespaces that were requested. -func (s *Service) GetAll(ctx context.Context, height uint64, namespaces []share.Namespace) ([]*Blob, error) { +func (s *Service) GetAll(ctx context.Context, height uint64, namespaces []libshare.Namespace) ([]*Blob, error) { header, err := s.headerGetter(ctx, height) if err != nil { return nil, err @@ -259,7 +260,7 @@ func (s *Service) GetAll(ctx context.Context, height uint64, namespaces []share. func (s *Service) getAll( ctx context.Context, header *header.ExtendedHeader, - namespaces []share.Namespace, + namespaces []libshare.Namespace, ) ([]*Blob, error) { height := header.Height() var ( @@ -269,13 +270,13 @@ func (s *Service) getAll( ) for i, namespace := range namespaces { wg.Add(1) - go func(i int, namespace share.Namespace) { + go func(i int, namespace libshare.Namespace) { log.Debugw("retrieving all blobs from", "namespace", namespace.String(), "height", height) defer wg.Done() blobs, err := s.getBlobs(ctx, namespace, header) if err != nil && !errors.Is(err, ErrBlobNotFound) { - log.Errorf("getting blobs for namespaceID(%s): %v", namespace.ID().String(), err) + log.Errorf("getting blobs for namespaceID(%s): %v", hex.EncodeToString(namespace.ID()), err) resultErr[i] = err } if len(blobs) > 0 { @@ -298,7 +299,7 @@ func (s *Service) getAll( func (s *Service) Included( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, proof *Proof, commitment Commitment, ) (_ bool, err error) { @@ -333,7 +334,7 @@ func (s *Service) Included( func (s *Service) retrieve( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, sharesParser *parser, ) (_ *Blob, _ *Proof, err error) { log.Infow("requesting blob", @@ -354,7 +355,11 @@ func (s *Service) retrieve( rowIndex := -1 for i, row := range header.DAH.RowRoots { - if !namespace.IsOutsideRange(row, row) { + outside, err := share.IsOutsideRange(namespace, row, row) + if err != nil { + return nil, nil, err + } + if !outside { rowIndex = i break } @@ -363,9 +368,9 @@ func (s *Service) retrieve( getCtx, getSharesSpan := tracer.Start(ctx, "get-shares-by-namespace") // collect shares for the requested namespace - namespacedShares, err := s.shareGetter.GetSharesByNamespace(getCtx, header, namespace) + namespacedShares, err := s.shareGetter.GetNamespaceData(getCtx, header, namespace) if err != nil { - if errors.Is(err, share.ErrNotFound) { + if errors.Is(err, shwap.ErrNotFound) { err = ErrBlobNotFound } getSharesSpan.SetStatus(codes.Error, err.Error()) @@ -377,7 +382,7 @@ func (s *Service) retrieve( attribute.Int64("eds-size", int64(len(header.DAH.RowRoots))))) var ( - appShares = make([]shares.Share, 0) + appShares = make([]libshare.Share, 0) proofs = make(Proof, 0) ) @@ -389,18 +394,14 @@ func (s *Service) retrieve( return nil, nil, ErrBlobNotFound } - appShares, err = toAppShares(row.Shares...) - if err != nil { - return nil, nil, err - } - + appShares = row.Shares proofs = append(proofs, row.Proof) index := row.Proof.Start() for { var ( isComplete bool - shrs []shares.Share + shrs []libshare.Share wasEmpty = sharesParser.isEmpty() ) @@ -458,11 +459,7 @@ func (s *Service) retrieve( err = ErrBlobNotFound for _, sh := range appShares { - ok, err := sh.IsPadding() - if err != nil { - return nil, nil, err - } - if !ok { + if !sh.IsPadding() { err = fmt.Errorf("incomplete blob with the "+ "namespace: %s detected at %d: %w", namespace.String(), height, err) log.Error(err) @@ -475,7 +472,7 @@ func (s *Service) retrieve( // them to Blobs. func (s *Service) getBlobs( ctx context.Context, - namespace share.Namespace, + namespace libshare.Namespace, header *header.ExtendedHeader, ) (_ []*Blob, err error) { ctx, span := tracer.Start(ctx, "get-blobs") @@ -501,7 +498,7 @@ func (s *Service) getBlobs( func (s *Service) GetCommitmentProof( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, shareCommitment []byte, ) (*CommitmentProof, error) { log.Debugw("proving share commitment", "height", height, "commitment", shareCommitment, "namespace", namespace) @@ -563,13 +560,13 @@ func (s *Service) GetCommitmentProof( func ProveCommitment( eds *rsmt2d.ExtendedDataSquare, - namespace share.Namespace, - blobShares []share.Share, + namespace libshare.Namespace, + blobShares []libshare.Share, ) (*CommitmentProof, error) { // find the blob shares in the EDS blobSharesStartIndex := -1 for index, share := range eds.FlattenedODS() { - if bytes2.Equal(share, blobShares[0]) { + if bytes.Equal(share, blobShares[0].ToBytes()) { blobSharesStartIndex = index } } @@ -577,11 +574,6 @@ func ProveCommitment( return nil, fmt.Errorf("couldn't find the blob shares in the ODS") } - nID, err := appns.From(namespace) - if err != nil { - return nil, err - } - log.Debugw( "generating the blob share proof for commitment", "start_share", @@ -591,8 +583,8 @@ func ProveCommitment( ) sharesProof, err := pkgproof.NewShareInclusionProofFromEDS( eds, - nID, - shares.NewRange(blobSharesStartIndex, blobSharesStartIndex+len(blobShares)), + namespace, + libshare.NewRange(blobSharesStartIndex, blobSharesStartIndex+len(blobShares)), ) if err != nil { return nil, err @@ -623,7 +615,7 @@ func ProveCommitment( ranges, err := nmt.ToLeafRanges( proof.Start(), proof.End(), - inclusion.SubTreeWidth(len(blobShares), appconsts.DefaultSubtreeRootThreshold), + inclusion.SubTreeWidth(len(blobShares), subtreeRootThreshold), ) if err != nil { return nil, err @@ -653,7 +645,7 @@ func ProveCommitment( // computeSubtreeRoots takes a set of shares and ranges and returns the corresponding subtree roots. // the offset is the number of shares that are before the subtree roots we're calculating. -func computeSubtreeRoots(shares []share.Share, ranges []nmt.LeafRange, offset int) ([][]byte, error) { +func computeSubtreeRoots(shares []libshare.Share, ranges []nmt.LeafRange, offset int) ([][]byte, error) { if len(shares) == 0 { return nil, fmt.Errorf("cannot compute subtree roots for an empty shares list") } @@ -668,11 +660,11 @@ func computeSubtreeRoots(shares []share.Share, ranges []nmt.LeafRange, offset in tree := nmt.New( appconsts.NewBaseHashFunc(), nmt.IgnoreMaxNamespace(true), - nmt.NamespaceIDSize(share.NamespaceSize), + nmt.NamespaceIDSize(libshare.NamespaceSize), ) for _, sh := range shares { leafData := make([]byte, 0) - leafData = append(append(leafData, share.GetNamespace(sh)...), sh...) + leafData = append(append(leafData, sh.Namespace().Bytes()...), sh.ToBytes()...) err := tree.Push(leafData) if err != nil { return nil, err diff --git a/blob/service_test.go b/blob/service_test.go index 80f2e54b0a..8c55d8824f 100644 --- a/blob/service_test.go +++ b/blob/service_test.go @@ -3,9 +3,11 @@ package blob import ( "bytes" "context" + "encoding/binary" "encoding/json" "errors" "fmt" + "slices" "sort" "testing" "time" @@ -15,27 +17,27 @@ import ( ds_sync "github.com/ipfs/go-datastore/sync" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/merkle" tmrand "github.com/tendermint/tendermint/libs/rand" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - pkgproof "github.com/celestiaorg/celestia-app/v2/pkg/proof" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + pkgproof "github.com/celestiaorg/celestia-app/v3/pkg/proof" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" "github.com/celestiaorg/go-header/store" - "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/inclusion" - squarens "github.com/celestiaorg/go-square/namespace" - "github.com/celestiaorg/go-square/shares" + "github.com/celestiaorg/go-square/merkle" + "github.com/celestiaorg/go-square/v2/inclusion" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" - "github.com/celestiaorg/celestia-node/blob/blobtest" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/header/headertest" - shareMock "github.com/celestiaorg/celestia-node/nodebuilder/share/mocks" + "github.com/celestiaorg/celestia-node/libs/utils" "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/getters" "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/getters/mock" ) func TestBlobService_Get(t *testing.T) { @@ -48,17 +50,20 @@ func TestBlobService_Get(t *testing.T) { blobSize3 = 12 ) - appBlobs, err := blobtest.GenerateV0Blobs([]int{blobSize0, blobSize1}, false) + libBlobs, err := libshare.GenerateV0Blobs([]int{blobSize0, blobSize1}, false) require.NoError(t, err) - blobsWithDiffNamespaces, err := convertBlobs(appBlobs...) + blobsWithDiffNamespaces, err := convertBlobs(libBlobs...) require.NoError(t, err) - appBlobs, err = blobtest.GenerateV0Blobs([]int{blobSize2, blobSize3}, true) + libBlobs, err = libshare.GenerateV0Blobs([]int{blobSize2, blobSize3}, true) require.NoError(t, err) - blobsWithSameNamespace, err := convertBlobs(appBlobs...) + blobsWithSameNamespace, err := convertBlobs(libBlobs...) require.NoError(t, err) - service := createService(ctx, t, append(blobsWithDiffNamespaces, blobsWithSameNamespace...)) + blobs := slices.Concat(blobsWithDiffNamespaces, blobsWithSameNamespace) + shares, err := BlobsToShares(blobs...) + require.NoError(t, err) + service := createService(ctx, t, shares) test := []struct { name string doFn func() (interface{}, error) @@ -87,7 +92,7 @@ func TestBlobService_Get(t *testing.T) { { name: "get all with the same namespace", doFn: func() (interface{}, error) { - return service.GetAll(ctx, 1, []share.Namespace{blobsWithSameNamespace[0].Namespace()}) + return service.GetAll(ctx, 1, []libshare.Namespace{blobsWithSameNamespace[0].Namespace()}) }, expectedResult: func(res interface{}, err error) { require.NoError(t, err) @@ -116,7 +121,7 @@ func TestBlobService_Get(t *testing.T) { blobsWithDiffNamespaces[1].Commitment, ) require.NoError(t, err) - b23, err := service.GetAll(ctx, 1, []share.Namespace{blobsWithSameNamespace[0].Namespace()}) + b23, err := service.GetAll(ctx, 1, []libshare.Namespace{blobsWithSameNamespace[0].Namespace()}) require.NoError(t, err) return []*Blob{b0, b1, b23[0], b23[1]}, nil }, @@ -128,7 +133,7 @@ func TestBlobService_Get(t *testing.T) { assert.Len(t, blobs, 4) sort.Slice(blobs, func(i, j int) bool { - val := bytes.Compare(blobs[i].NamespaceId, blobs[j].NamespaceId) + val := bytes.Compare(blobs[i].Namespace().ID(), blobs[j].Namespace().ID()) return val < 0 }) @@ -140,22 +145,24 @@ func TestBlobService_Get(t *testing.T) { shareOffset := 0 for i := range blobs { row, col := calculateIndex(len(h.DAH.RowRoots), blobs[i].index) - sh, err := service.shareGetter.GetShare(ctx, h, row, col) + idx := shwap.SampleCoords{Row: row, Col: col} + require.NoError(t, err) + smpls, err := service.shareGetter.GetSamples(ctx, h, []shwap.SampleCoords{idx}) require.NoError(t, err) - require.True(t, bytes.Equal(sh, resultShares[shareOffset]), + require.True(t, bytes.Equal(smpls[0].Share.ToBytes(), resultShares[shareOffset].ToBytes()), fmt.Sprintf("issue on %d attempt. ROW:%d, COL: %d, blobIndex:%d", i, row, col, blobs[i].index), ) - shareOffset += shares.SparseSharesNeeded(uint32(len(blobs[i].Data))) + shareOffset += libshare.SparseSharesNeeded(uint32(len(blobs[i].Data()))) } }, }, { name: "get all with different namespaces", doFn: func() (interface{}, error) { - nid, err := share.NewBlobNamespaceV0(tmrand.Bytes(7)) + nid, err := libshare.NewV0Namespace(tmrand.Bytes(7)) require.NoError(t, err) b, err := service.GetAll(ctx, 1, - []share.Namespace{ + []libshare.Namespace{ blobsWithDiffNamespaces[0].Namespace(), nid, blobsWithDiffNamespaces[1].Namespace(), }, @@ -170,8 +177,9 @@ func TestBlobService_Get(t *testing.T) { assert.NotEmpty(t, blobs) assert.Len(t, blobs, 2) // check the order - require.True(t, bytes.Equal(blobs[0].Namespace(), blobsWithDiffNamespaces[0].Namespace())) - require.True(t, bytes.Equal(blobs[1].Namespace(), blobsWithDiffNamespaces[1].Namespace())) + + require.True(t, blobs[0].Namespace().Equals(blobsWithDiffNamespaces[0].Namespace())) + require.True(t, blobs[1].Namespace().Equals(blobsWithDiffNamespaces[1].Namespace())) }, }, { @@ -194,9 +202,9 @@ func TestBlobService_Get(t *testing.T) { { name: "get invalid blob", doFn: func() (interface{}, error) { - appBlob, err := blobtest.GenerateV0Blobs([]int{10}, false) + libBlob, err := libshare.GenerateV0Blobs([]int{10}, false) require.NoError(t, err) - blob, err := convertBlobs(appBlob...) + blob, err := convertBlobs(libBlob...) require.NoError(t, err) b, err := service.Get(ctx, 1, blob[0].Namespace(), blob[0].Commitment) @@ -228,13 +236,13 @@ func TestBlobService_Get(t *testing.T) { proof, ok := res.(*Proof) assert.True(t, ok) - verifyFn := func(t *testing.T, rawShares [][]byte, proof *Proof, namespace share.Namespace) { + verifyFn := func(t *testing.T, rawShares [][]byte, proof *Proof, namespace libshare.Namespace) { for _, row := range header.DAH.RowRoots { to := 0 for _, p := range *proof { from := to to = p.End() - p.Start() + from - eq := p.VerifyInclusion(share.NewSHA256Hasher(), namespace.ToNMT(), rawShares[from:to], row) + eq := p.VerifyInclusion(share.NewSHA256Hasher(), namespace.Bytes(), rawShares[from:to], row) if eq == true { return } @@ -245,7 +253,7 @@ func TestBlobService_Get(t *testing.T) { rawShares, err := BlobsToShares(blobsWithDiffNamespaces[1]) require.NoError(t, err) - verifyFn(t, rawShares, proof, blobsWithDiffNamespaces[1].Namespace()) + verifyFn(t, libshare.ToBytes(rawShares), proof, blobsWithDiffNamespaces[1].Namespace()) }, }, { @@ -294,9 +302,9 @@ func TestBlobService_Get(t *testing.T) { { name: "not included", doFn: func() (interface{}, error) { - appBlob, err := blobtest.GenerateV0Blobs([]int{10}, false) + libBlob, err := libshare.GenerateV0Blobs([]int{10}, false) require.NoError(t, err) - blob, err := convertBlobs(appBlob...) + blob, err := convertBlobs(libBlob...) require.NoError(t, err) proof, err := service.GetProof(ctx, 1, @@ -350,9 +358,9 @@ func TestBlobService_Get(t *testing.T) { { name: "empty result and err when blobs were not found ", doFn: func() (interface{}, error) { - nid, err := share.NewBlobNamespaceV0(tmrand.Bytes(squarens.NamespaceVersionZeroIDSize)) + nid, err := libshare.NewV0Namespace(tmrand.Bytes(libshare.NamespaceVersionZeroIDSize)) require.NoError(t, err) - return service.GetAll(ctx, 1, []share.Namespace{nid}) + return service.GetAll(ctx, 1, []libshare.Namespace{nid}) }, expectedResult: func(i interface{}, err error) { blobs, ok := i.([]*Blob) @@ -361,6 +369,19 @@ func TestBlobService_Get(t *testing.T) { assert.NoError(t, err) }, }, + { + name: "err during proof request", + doFn: func() (interface{}, error) { + proof, err := service.GetProof(ctx, 1, + blobsWithDiffNamespaces[0].Namespace(), + blobsWithDiffNamespaces[1].Commitment, + ) + return proof, err + }, + expectedResult: func(_ interface{}, err error) { + require.Error(t, err) + }, + }, { name: "marshal proof", doFn: func() (interface{}, error) { @@ -390,23 +411,23 @@ func TestBlobService_Get(t *testing.T) { name: "internal error", doFn: func() (interface{}, error) { ctrl := gomock.NewController(t) - shareService := service.shareGetter - shareGetterMock := shareMock.NewMockModule(ctrl) - shareGetterMock.EXPECT(). - GetSharesByNamespace(gomock.Any(), gomock.Any(), gomock.Any()). + innerGetter := service.shareGetter + getterWrapper := mock.NewMockGetter(ctrl) + getterWrapper.EXPECT(). + GetNamespaceData(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn( func( - ctx context.Context, h *header.ExtendedHeader, ns share.Namespace, - ) (share.NamespacedShares, error) { + ctx context.Context, h *header.ExtendedHeader, ns libshare.Namespace, + ) (shwap.NamespaceData, error) { if ns.Equals(blobsWithDiffNamespaces[0].Namespace()) { return nil, errors.New("internal error") } - return shareService.GetSharesByNamespace(ctx, h, ns) + return innerGetter.GetNamespaceData(ctx, h, ns) }).AnyTimes() - service.shareGetter = shareGetterMock + service.shareGetter = getterWrapper return service.GetAll(ctx, 1, - []share.Namespace{ + []libshare.Namespace{ blobsWithDiffNamespaces[0].Namespace(), blobsWithSameNamespace[0].Namespace(), }, @@ -436,47 +457,27 @@ func TestBlobService_Get(t *testing.T) { // But to satisfy the rule of eds creating, padding namespace share is placed between // blobs. Test ensures that blob service will skip padding share and return the correct blob. func TestService_GetSingleBlobWithoutPadding(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) t.Cleanup(cancel) - appBlob, err := blobtest.GenerateV0Blobs([]int{9, 5}, true) + libBlob, err := libshare.GenerateV0Blobs([]int{9, 5}, true) require.NoError(t, err) - blobs, err := convertBlobs(appBlob...) + blobs, err := convertBlobs(libBlob...) require.NoError(t, err) - ns1, ns2 := blobs[0].Namespace().ToAppNamespace(), blobs[1].Namespace().ToAppNamespace() - - padding0, err := shares.NamespacePaddingShare(ns1, appconsts.ShareVersionZero) + padding0, err := libshare.NamespacePaddingShare(blobs[0].Namespace(), libshare.ShareVersionZero) require.NoError(t, err) - padding1, err := shares.NamespacePaddingShare(ns2, appconsts.ShareVersionZero) + padding1, err := libshare.NamespacePaddingShare(blobs[1].Namespace(), libshare.ShareVersionZero) require.NoError(t, err) rawShares0, err := BlobsToShares(blobs[0]) require.NoError(t, err) rawShares1, err := BlobsToShares(blobs[1]) require.NoError(t, err) - rawShares := make([][]byte, 0) - rawShares = append(rawShares, append(rawShares0, padding0.ToBytes())...) - rawShares = append(rawShares, append(rawShares1, padding1.ToBytes())...) - - bs := ipld.NewMemBlockservice() - batching := ds_sync.MutexWrap(ds.NewMapDatastore()) - headerStore, err := store.NewStore[*header.ExtendedHeader](batching) - require.NoError(t, err) - eds, err := ipld.AddShares(ctx, rawShares, bs) - require.NoError(t, err) - - h := headertest.ExtendedHeaderFromEDS(t, 1, eds) - err = headerStore.Init(ctx, h) - require.NoError(t, err) - - fn := func(ctx context.Context, height uint64) (*header.ExtendedHeader, error) { - return headerStore.GetByHeight(ctx, height) - } - fn2 := func(ctx context.Context) (<-chan *header.ExtendedHeader, error) { - return nil, fmt.Errorf("not implemented") - } - service := NewService(nil, getters.NewIPLDGetter(bs), fn, fn2) + rawShares := make([]libshare.Share, 0) + rawShares = append(rawShares, append(rawShares0, padding0)...) + rawShares = append(rawShares, append(rawShares1, padding1)...) + service := createService(ctx, t, rawShares) newBlob, err := service.Get(ctx, 1, blobs[1].Namespace(), blobs[1].Commitment) require.NoError(t, err) @@ -484,11 +485,17 @@ func TestService_GetSingleBlobWithoutPadding(t *testing.T) { resultShares, err := BlobsToShares(newBlob) require.NoError(t, err) + + h, err := service.headerGetter(ctx, 1) + require.NoError(t, err) row, col := calculateIndex(len(h.DAH.RowRoots), newBlob.index) - sh, err := service.shareGetter.GetShare(ctx, h, row, col) + idx := shwap.SampleCoords{Row: row, Col: col} + require.NoError(t, err) + + smpls, err := service.shareGetter.GetSamples(ctx, h, []shwap.SampleCoords{idx}) require.NoError(t, err) - assert.Equal(t, sh, resultShares[0]) + assert.Equal(t, smpls[0].Share, resultShares[0]) } func TestService_Get(t *testing.T) { @@ -497,12 +504,14 @@ func TestService_Get(t *testing.T) { sizes := []int{1, 6, 3, 2, 4, 6, 8, 2, 15, 17} - appBlobs, err := blobtest.GenerateV0Blobs(sizes, true) + libBlobs, err := libshare.GenerateV0Blobs(sizes, true) require.NoError(t, err) - blobs, err := convertBlobs(appBlobs...) + blobs, err := convertBlobs(libBlobs...) require.NoError(t, err) - service := createService(ctx, t, blobs) + shares, err := BlobsToShares(blobs...) + require.NoError(t, err) + service := createService(ctx, t, shares) h, err := service.headerGetter(ctx, 1) require.NoError(t, err) @@ -517,11 +526,14 @@ func TestService_Get(t *testing.T) { assert.Equal(t, b.Commitment, blob.Commitment) row, col := calculateIndex(len(h.DAH.RowRoots), b.index) - sh, err := service.shareGetter.GetShare(ctx, h, row, col) + idx := shwap.SampleCoords{Row: row, Col: col} require.NoError(t, err) - assert.Equal(t, sh, resultShares[shareOffset], fmt.Sprintf("issue on %d attempt", i)) - shareOffset += shares.SparseSharesNeeded(uint32(len(blob.Data))) + smpls, err := service.shareGetter.GetSamples(ctx, h, []shwap.SampleCoords{idx}) + require.NoError(t, err) + + assert.Equal(t, smpls[0].Share, resultShares[shareOffset], fmt.Sprintf("issue on %d attempt", i)) + shareOffset += libshare.SparseSharesNeeded(uint32(len(blob.Data()))) } } @@ -532,122 +544,100 @@ func TestService_GetAllWithoutPadding(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) t.Cleanup(cancel) - appBlob, err := blobtest.GenerateV0Blobs([]int{9, 5, 15, 4, 24}, true) + libBlob, err := libshare.GenerateV0Blobs([]int{9, 5, 15, 4, 24}, true) require.NoError(t, err) - blobs, err := convertBlobs(appBlob...) + blobs, err := convertBlobs(libBlob...) require.NoError(t, err) - var ( - ns = blobs[0].Namespace().ToAppNamespace() - rawShares = make([][]byte, 0) - ) + rawShares := make([]libshare.Share, 0) - padding, err := shares.NamespacePaddingShare(ns, appconsts.ShareVersionZero) + require.NoError(t, err) + padding, err := libshare.NamespacePaddingShare(blobs[0].Namespace(), libshare.ShareVersionZero) require.NoError(t, err) for i := 0; i < 2; i++ { sh, err := BlobsToShares(blobs[i]) require.NoError(t, err) - rawShares = append(rawShares, append(sh, padding.ToBytes())...) + rawShares = append(rawShares, append(sh, padding)...) } sh, err := BlobsToShares(blobs[2]) require.NoError(t, err) - rawShares = append(rawShares, append(sh, padding.ToBytes(), padding.ToBytes())...) + rawShares = append(rawShares, append(sh, padding, padding)...) sh, err = BlobsToShares(blobs[3]) require.NoError(t, err) - rawShares = append(rawShares, append(sh, padding.ToBytes(), padding.ToBytes(), padding.ToBytes())...) + rawShares = append(rawShares, append(sh, padding, padding, padding)...) sh, err = BlobsToShares(blobs[4]) require.NoError(t, err) rawShares = append(rawShares, sh...) + service := createService(ctx, t, rawShares) - bs := ipld.NewMemBlockservice() - require.NoError(t, err) - eds, err := ipld.AddShares(ctx, rawShares, bs) - require.NoError(t, err) - - h := headertest.ExtendedHeaderFromEDS(t, 1, eds) - - fn := func(ctx context.Context, height uint64) (*header.ExtendedHeader, error) { - return h, nil - } - fn2 := func(ctx context.Context) (<-chan *header.ExtendedHeader, error) { - return nil, fmt.Errorf("not implemented") - } - service := NewService(nil, getters.NewIPLDGetter(bs), fn, fn2) - - newBlobs, err := service.GetAll(ctx, 1, []share.Namespace{blobs[0].Namespace()}) + newBlobs, err := service.GetAll(ctx, 1, []libshare.Namespace{blobs[0].Namespace()}) require.NoError(t, err) assert.Equal(t, len(newBlobs), len(blobs)) resultShares, err := BlobsToShares(newBlobs...) require.NoError(t, err) + h, err := service.headerGetter(ctx, 1) + require.NoError(t, err) shareOffset := 0 for i, blob := range newBlobs { require.True(t, blobs[i].compareCommitments(blob.Commitment)) row, col := calculateIndex(len(h.DAH.RowRoots), blob.index) - sh, err := service.shareGetter.GetShare(ctx, h, row, col) + idx := shwap.SampleCoords{Row: row, Col: col} + require.NoError(t, err) + + smpls, err := service.shareGetter.GetSamples(ctx, h, []shwap.SampleCoords{idx}) require.NoError(t, err) - assert.Equal(t, sh, resultShares[shareOffset]) - shareOffset += shares.SparseSharesNeeded(uint32(len(blob.Data))) + assert.Equal(t, smpls[0].Share, resultShares[shareOffset]) + shareOffset += libshare.SparseSharesNeeded(uint32(len(blob.Data()))) } } func TestAllPaddingSharesInEDS(t *testing.T) { - nid, err := share.NewBlobNamespaceV0(tmrand.Bytes(7)) + nid, err := libshare.NewV0Namespace(tmrand.Bytes(7)) require.NoError(t, err) - padding, err := shares.NamespacePaddingShare(nid.ToAppNamespace(), appconsts.ShareVersionZero) + + padding, err := libshare.NamespacePaddingShare(nid, libshare.ShareVersionZero) require.NoError(t, err) - rawShares := make([]share.Share, 16) + rawShares := make([]libshare.Share, 16) for i := 0; i < 16; i++ { - rawShares[i] = padding.ToBytes() + rawShares[i] = padding } ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) t.Cleanup(cancel) - bs := ipld.NewMemBlockservice() - require.NoError(t, err) - eds, err := ipld.AddShares(ctx, rawShares, bs) - require.NoError(t, err) - - h := headertest.ExtendedHeaderFromEDS(t, 1, eds) - - fn := func(ctx context.Context, height uint64) (*header.ExtendedHeader, error) { - return h, nil - } - fn2 := func(ctx context.Context) (<-chan *header.ExtendedHeader, error) { - return nil, fmt.Errorf("not implemented") - } - service := NewService(nil, getters.NewIPLDGetter(bs), fn, fn2) - newBlobs, err := service.GetAll(ctx, 1, []share.Namespace{nid}) + service := createService(ctx, t, rawShares) + newBlobs, err := service.GetAll(ctx, 1, []libshare.Namespace{nid}) require.NoError(t, err) assert.Empty(t, newBlobs) } func TestSkipPaddingsAndRetrieveBlob(t *testing.T) { - nid, err := share.NewBlobNamespaceV0(tmrand.Bytes(7)) - require.NoError(t, err) - padding, err := shares.NamespacePaddingShare(nid.ToAppNamespace(), appconsts.ShareVersionZero) + nid := libshare.RandomBlobNamespace() + padding, err := libshare.NamespacePaddingShare(nid, libshare.ShareVersionZero) require.NoError(t, err) - rawShares := make([]share.Share, 0, 64) + rawShares := make([]libshare.Share, 0, 64) for i := 0; i < 58; i++ { - rawShares = append(rawShares, padding.ToBytes()) + rawShares = append(rawShares, padding) } - appBlob, err := blobtest.GenerateV0Blobs([]int{6}, true) + size := rawBlobSize(libshare.FirstSparseShareContentSize * 6) + ns, err := libshare.NewNamespace(nid.Version(), nid.ID()) + require.NoError(t, err) + data := tmrand.Bytes(size) + libBlob, err := libshare.NewBlob(ns, data, libshare.ShareVersionZero, nil) require.NoError(t, err) - appBlob[0].NamespaceVersion = uint32(nid[0]) - appBlob[0].NamespaceId = nid[1:] - blobs, err := convertBlobs(appBlob...) + blobs, err := convertBlobs(libBlob) require.NoError(t, err) sh, err := BlobsToShares(blobs[0]) require.NoError(t, err) @@ -657,21 +647,8 @@ func TestSkipPaddingsAndRetrieveBlob(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) t.Cleanup(cancel) - bs := ipld.NewMemBlockservice() - require.NoError(t, err) - eds, err := ipld.AddShares(ctx, rawShares, bs) - require.NoError(t, err) - - h := headertest.ExtendedHeaderFromEDS(t, 1, eds) - - fn := func(ctx context.Context, height uint64) (*header.ExtendedHeader, error) { - return h, nil - } - fn2 := func(ctx context.Context) (<-chan *header.ExtendedHeader, error) { - return nil, fmt.Errorf("not implemented") - } - service := NewService(nil, getters.NewIPLDGetter(bs), fn, fn2) - newBlob, err := service.GetAll(ctx, 1, []share.Namespace{nid}) + service := createService(ctx, t, rawShares) + newBlob, err := service.GetAll(ctx, 1, []libshare.Namespace{nid}) require.NoError(t, err) require.Len(t, newBlob, 1) require.True(t, newBlob[0].compareCommitments(blobs[0].Commitment)) @@ -681,9 +658,9 @@ func TestService_Subscribe(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) t.Cleanup(cancel) - appBlobs, err := blobtest.GenerateV0Blobs([]int{16, 16, 16}, true) + libBlobs, err := libshare.GenerateV0Blobs([]int{16, 16, 16}, true) require.NoError(t, err) - blobs, err := convertBlobs(appBlobs...) + blobs, err := convertBlobs(libBlobs...) require.NoError(t, err) service := createServiceWithSub(ctx, t, blobs) @@ -699,7 +676,7 @@ func TestService_Subscribe(t *testing.T) { select { case resp := <-subCh: assert.Equal(t, i+1, resp.Height) - assert.Equal(t, blobs[i].Data, resp.Blobs[0].Data) + assert.Equal(t, blobs[i].Data(), resp.Blobs[0].Data()) case <-time.After(time.Second * 2): t.Fatalf("timeout waiting for subscription response %d", i) } @@ -707,7 +684,7 @@ func TestService_Subscribe(t *testing.T) { }) t.Run("subscription with no matching blobs", func(t *testing.T) { - ns, err := share.NewBlobNamespaceV0([]byte("nonexist")) + ns, err := libshare.NewV0Namespace([]byte("nonexist")) require.NoError(t, err) subCh, err := service.Subscribe(ctx, ns) @@ -726,8 +703,11 @@ func TestService_Subscribe(t *testing.T) { }) t.Run("subscription cancellation", func(t *testing.T) { + subCtx, subCancel := context.WithTimeout(ctx, time.Second*2) + t.Cleanup(subCancel) + ns := blobs[0].Namespace() - subCtx, subCancel := context.WithCancel(ctx) + subCh, err := service.Subscribe(subCtx, ns) require.NoError(t, err) @@ -735,15 +715,20 @@ func TestService_Subscribe(t *testing.T) { select { case <-subCh: subCancel() - case <-time.After(time.Second * 2): + case <-ctx.Done(): t.Fatal("timeout waiting for first subscription response") } - select { - case _, ok := <-subCh: - assert.False(t, ok, "expected subscription channel to be closed") - case <-time.After(time.Second * 2): - t.Fatal("timeout waiting for subscription channel to close") + for { + select { + case _, ok := <-subCh: + if !ok { + // channel closed as expected + return + } + case <-ctx.Done(): + t.Fatal("timeout waiting for subscription channel to close") + } } }) @@ -778,18 +763,18 @@ func TestService_Subscribe_MultipleNamespaces(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) t.Cleanup(cancel) - appBlobs1, err := blobtest.GenerateV0Blobs([]int{100, 100}, true) + libBlobs1, err := libshare.GenerateV0Blobs([]int{100, 100}, true) require.NoError(t, err) - appBlobs2, err := blobtest.GenerateV0Blobs([]int{100, 100}, true) - for i := range appBlobs2 { - // if we don't do this, appBlobs1 and appBlobs2 will share a NS - appBlobs2[i].GetNamespaceId()[len(appBlobs2[i].GetNamespaceId())-1] = 0xDE + libBlobs2, err := libshare.GenerateV0Blobs([]int{100, 100}, true) + for i := range libBlobs2 { + // if we don't do this, libBlobs1 and libBlobs2 will share a NS + libBlobs2[i].Namespace().ID()[len(libBlobs2[i].Namespace().ID())-1] = 0xDE } require.NoError(t, err) - blobs1, err := convertBlobs(appBlobs1...) + blobs1, err := convertBlobs(libBlobs1...) require.NoError(t, err) - blobs2, err := convertBlobs(appBlobs2...) + blobs2, err := convertBlobs(libBlobs2...) require.NoError(t, err) //nolint: gocritic @@ -839,13 +824,15 @@ func TestService_Subscribe_MultipleNamespaces(t *testing.T) { func BenchmarkGetByCommitment(b *testing.B) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) b.Cleanup(cancel) - appBlobs, err := blobtest.GenerateV0Blobs([]int{32, 32}, true) + libBlobs, err := libshare.GenerateV0Blobs([]int{32, 32}, true) require.NoError(b, err) - blobs, err := convertBlobs(appBlobs...) + blobs, err := convertBlobs(libBlobs...) require.NoError(b, err) - service := createService(ctx, b, blobs) + shares, err := BlobsToShares(blobs...) + require.NoError(b, err) + service := createService(ctx, b, shares) indexer := &parser{} b.ResetTimer() for i := 0; i < b.N; i++ { @@ -897,20 +884,48 @@ func createServiceWithSub(ctx context.Context, t testing.TB, blobs []*Blob) *Ser }() return headerChan, nil } - return NewService(nil, getters.NewIPLDGetter(bs), fn, fn2) + ctrl := gomock.NewController(t) + shareGetter := mock.NewMockGetter(ctrl) + + shareGetter.EXPECT().GetNamespaceData(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes(). + DoAndReturn(func(ctx context.Context, h *header.ExtendedHeader, ns libshare.Namespace) (shwap.NamespaceData, error) { + idx := int(h.Height()) - 1 + accessor := &eds.Rsmt2D{ExtendedDataSquare: edsses[idx]} + nd, err := eds.NamespaceData(ctx, accessor, ns) + return nd, err + }) + return NewService(nil, shareGetter, fn, fn2) } -func createService(ctx context.Context, t testing.TB, blobs []*Blob) *Service { - bs := ipld.NewMemBlockservice() +func createService(ctx context.Context, t testing.TB, shares []libshare.Share) *Service { + odsSize := int(utils.SquareSize(len(shares))) + square, err := rsmt2d.ComputeExtendedDataSquare( + libshare.ToBytes(shares), + share.DefaultRSMT2DCodec(), + wrapper.NewConstructor(uint64(odsSize))) + require.NoError(t, err) + + accessor := &eds.Rsmt2D{ExtendedDataSquare: square} + ctrl := gomock.NewController(t) + shareGetter := mock.NewMockGetter(ctrl) + shareGetter.EXPECT().GetNamespaceData(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes(). + DoAndReturn(func(ctx context.Context, h *header.ExtendedHeader, ns libshare.Namespace) (shwap.NamespaceData, error) { + nd, err := eds.NamespaceData(ctx, accessor, ns) + return nd, err + }) + shareGetter.EXPECT().GetSamples(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes(). + DoAndReturn(func(ctx context.Context, h *header.ExtendedHeader, + indices []shwap.SampleCoords, + ) ([]shwap.Sample, error) { + smpl, err := accessor.Sample(ctx, indices[0]) + return []shwap.Sample{smpl}, err + }) + + // create header and put it into the store + h := headertest.ExtendedHeaderFromEDS(t, 1, square) batching := ds_sync.MutexWrap(ds.NewMapDatastore()) headerStore, err := store.NewStore[*header.ExtendedHeader](batching) require.NoError(t, err) - rawShares, err := BlobsToShares(blobs...) - require.NoError(t, err) - eds, err := ipld.AddShares(ctx, rawShares, bs) - require.NoError(t, err) - - h := headertest.ExtendedHeaderFromEDS(t, 1, eds) err = headerStore.Init(ctx, h) require.NoError(t, err) @@ -920,7 +935,7 @@ func createService(ctx context.Context, t testing.TB, blobs []*Blob) *Service { fn2 := func(ctx context.Context) (<-chan *header.ExtendedHeader, error) { return nil, fmt.Errorf("not implemented") } - return NewService(nil, getters.NewIPLDGetter(bs), fn, fn2) + return NewService(nil, shareGetter, fn, fn2) } // TestProveCommitmentAllCombinations tests proving all the commitments in a block. @@ -950,12 +965,12 @@ func proveAndVerifyShareCommitments(t *testing.T, blobSize int) { msgs, blobs, nss, eds, _, _, dataRoot := edstest.GenerateTestBlock(t, blobSize, 10) for msgIndex, msg := range msgs { t.Run(fmt.Sprintf("msgIndex=%d", msgIndex), func(t *testing.T) { - blb, err := NewBlob(uint8(blobs[msgIndex].GetShareVersion()), nss[msgIndex].Bytes(), blobs[msgIndex].GetData()) + blb, err := NewBlob(blobs[msgIndex].ShareVersion(), nss[msgIndex], blobs[msgIndex].Data(), nil) require.NoError(t, err) blobShares, err := BlobsToShares(blb) require.NoError(t, err) // compute the commitment - actualCommitmentProof, err := ProveCommitment(eds, nss[msgIndex].Bytes(), blobShares) + actualCommitmentProof, err := ProveCommitment(eds, nss[msgIndex], blobShares) require.NoError(t, err) // make sure the actual commitment attests to the data @@ -968,7 +983,7 @@ func proveAndVerifyShareCommitments(t *testing.T, blobSize int) { require.True(t, valid) // generate an expected proof and verify it's valid - expectedCommitmentProof := generateCommitmentProofFromBlock(t, eds, nss[msgIndex].Bytes(), blobs[msgIndex], dataRoot) + expectedCommitmentProof := generateCommitmentProofFromBlock(t, eds, nss[msgIndex], blobs[msgIndex], dataRoot) require.NoError(t, expectedCommitmentProof.Validate()) valid, err = expectedCommitmentProof.Verify( dataRoot, @@ -992,15 +1007,15 @@ func proveAndVerifyShareCommitments(t *testing.T, blobSize int) { func generateCommitmentProofFromBlock( t *testing.T, eds *rsmt2d.ExtendedDataSquare, - ns share.Namespace, - blob *blob.Blob, + ns libshare.Namespace, + blob *libshare.Blob, dataRoot []byte, ) CommitmentProof { // create the blob from the data - blb, err := NewBlob( - uint8(blob.GetShareVersion()), + blb, err := NewBlob(blob.ShareVersion(), ns, - blob.GetData(), + blob.Data(), + nil, ) require.NoError(t, err) @@ -1011,7 +1026,7 @@ func generateCommitmentProofFromBlock( // find the first share of the blob in the ODS startShareIndex := -1 for i, sh := range eds.FlattenedODS() { - if bytes.Equal(sh, blobShares[0]) { + if bytes.Equal(sh, blobShares[0].ToBytes()) { startShareIndex = i break } @@ -1021,8 +1036,8 @@ func generateCommitmentProofFromBlock( // create an inclusion proof of the blob using the share range instead of the commitment sharesProof, err := pkgproof.NewShareInclusionProofFromEDS( eds, - ns.ToAppNamespace(), - shares.NewRange(startShareIndex, startShareIndex+len(blobShares)), + ns, + libshare.NewRange(startShareIndex, startShareIndex+len(blobShares)), ) require.NoError(t, err) require.NoError(t, sharesProof.Validate(dataRoot)) @@ -1034,7 +1049,7 @@ func generateCommitmentProofFromBlock( ranges, err := nmt.ToLeafRanges( int(proof.Start), int(proof.End), - inclusion.SubTreeWidth(len(blobShares), appconsts.DefaultSubtreeRootThreshold), + inclusion.SubTreeWidth(len(blobShares), subtreeRootThreshold), ) require.NoError(t, err) roots, err := computeSubtreeRoots( @@ -1064,3 +1079,13 @@ func generateCommitmentProofFromBlock( return commitmentProof } + +func rawBlobSize(totalSize int) int { + return totalSize - delimLen(uint64(totalSize)) +} + +// delimLen calculates the length of the delimiter for a given unit size +func delimLen(size uint64) int { + lenBuf := make([]byte, binary.MaxVarintLen64) + return binary.PutUvarint(lenBuf, size) +} diff --git a/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65203220736861726573.json b/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65203220736861726573.json new file mode 100755 index 0000000000..9f8db72d93 --- /dev/null +++ b/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65203220736861726573.json @@ -0,0 +1 @@ +[{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52oHeuImEc7RFID2hHj265dSFSVufOxiuGSif7xyyy8S8","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52ooSzWcKz7xnJHYxwcMfgRUOwToc++AxQP2NZnsChr+A","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52g+d0EwnW6jMox0p2njI0Sq/lGhAwZU3db7s4ukdtQ4P"],"subtree_root_proofs":[{"start":2,"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3zyS+dGya/QiFSASvHRVLPRvC472P5ZjP07i2mrnex5","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5vfo3RPSbjeXfY4UtRaGOpxtxcCl9Plvp+vz0VHI33g7","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5v2WnF5SNkNcgkCrccW2Kv7b2z5iSqCr64P2eCaKsffn","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5iRaSxDzlg6Mtq0+LgIBNasiOTaeQcnqGoHoIw2vyN2c","/////////////////////////////////////////////////////////////////////////////5wcCTpZvE0S4+peb+IZFHDHaPdqDosLK4qwh+wextWG"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5pHVTsd/j4nwBLfYA9TfmtRjGi771Yki9q31nstfVQJu"],"proofs":[{"total":64,"index":4,"leaf_hash":"HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","aunts":["o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":4,"end_row":4},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52oHeuImEc7RFID2hHj265dSFSVufOxiuGSif7xyyy8S8","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52ooSzWcKz7xnJHYxwcMfgRUOwToc++AxQP2NZnsChr+A","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52g+d0EwnW6jMox0p2njI0Sq/lGhAwZU3db7s4ukdtQ4P"],"subtree_root_proofs":[{"start":2,"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3zyS+dGya/QiFSASvHRVLPRvC472P5ZjP07i2mrnex5","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5vfo3RPSbjeXfY4UtRaGOpxtxcCl9Plvp+vz0VHI33g7","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5v2WnF5SNkNcgkCrccW2Kv7b2z5iSqCr64P2eCaKsffn","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5iRaSxDzlg6Mtq0+LgIBNasiOTaeQcnqGoHoIw2vyN2c","/////////////////////////////////////////////////////////////////////////////5wcCTpZvE0S4+peb+IZFHDHaPdqDosLK4qwh+wextWG"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5pHVTsd/j4nwBLfYA9TfmtRjGi771Yki9q31nstfVQJu"],"proofs":[{"total":64,"index":4,"leaf_hash":"HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","aunts":["o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":4,"end_row":4},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve/OpA/iWcYx5vUGA+ajr9XckesE3DnPD2wMLwHvYXjO9","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve89sxEXMlz0ywfANhZHYDZomXJvfYYjLJIgTJHLIEcC3","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve+IitkdM+Yjfr7jTKjL253wgFi7+DB7SUdps70zhlb9O","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcvey7JPS9owwMHYvSEG7D7hAagH9bpk+Eooya+ThP/XlVA","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve0vyQOWlH4xBxZkJOWxPdEzU0sqQ5qmE8HRPHDb2FeXV"],"subtree_root_proofs":[{"start":7,"end":12,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNf848qhakZ0J1cM7sVJ0Y1JFVPRet8YU4kyIa42rr3N","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPuImrN0+NDnH+U50F1clIs8CRNb0cTAsS+rg6nRVeU9","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFMV8HG8tkpzHDdG3z2ZvLLQGcYTqHcPYGd2P10v4jVd","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnEsNjlFDYa2mZ79Mh9iX2VtTqMPhYt5QgZwIs8UY9Wh2","/////////////////////////////////////////////////////////////////////////////4hWdR2Ii7EJrAl532ACtqACyRkeqij9FouQ5st+ZfO3"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcvew==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnCEbJz0BNo73xah/NNLONhU6lfAHlfC7VTv2/UYH6GS/"],"proofs":[{"total":64,"leaf_hash":"yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","aunts":["xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}]},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve/OpA/iWcYx5vUGA+ajr9XckesE3DnPD2wMLwHvYXjO9","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve89sxEXMlz0ywfANhZHYDZomXJvfYYjLJIgTJHLIEcC3","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve+IitkdM+Yjfr7jTKjL253wgFi7+DB7SUdps70zhlb9O","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcvey7JPS9owwMHYvSEG7D7hAagH9bpk+Eooya+ThP/XlVA","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve0vyQOWlH4xBxZkJOWxPdEzU0sqQ5qmE8HRPHDb2FeXV"],"subtree_root_proofs":[{"start":7,"end":12,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNf848qhakZ0J1cM7sVJ0Y1JFVPRet8YU4kyIa42rr3N","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPuImrN0+NDnH+U50F1clIs8CRNb0cTAsS+rg6nRVeU9","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFMV8HG8tkpzHDdG3z2ZvLLQGcYTqHcPYGd2P10v4jVd","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnEsNjlFDYa2mZ79Mh9iX2VtTqMPhYt5QgZwIs8UY9Wh2","/////////////////////////////////////////////////////////////////////////////4hWdR2Ii7EJrAl532ACtqACyRkeqij9FouQ5st+ZfO3"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcvew==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnCEbJz0BNo73xah/NNLONhU6lfAHlfC7VTv2/UYH6GS/"],"proofs":[{"total":64,"leaf_hash":"yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","aunts":["xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}]},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+r/FKkjvRbxGxvXwctupv6uI5IFwBgRUPyfVjBu9/U84","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+hsPbxDEqMELTQw4ErdPuIirkF7JDrxGdusib5J6RDcB","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+jv/osIC2ya9MWm1KB1ZEAcPn2FXvlRGK3eLDs0Hy2Q/","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+k8pQYudXC9yt6rT1i9JGCHHmcrhGkZ1EfC5OxN5Lf7V","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+tC1eEVsJggh/dwYA7243yudq6J3p51RbOTz6ASaMztJ","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+sPi0F2eW+OWKLWQqplLW3oIFvcgVpUHIq+5ZbNh2RM9","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+o7JugfzvIzbrj3DNXLcbGQQTejXUe0vX3I8RCVHTdCp"],"subtree_root_proofs":[{"start":8,"end":15,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQyNwFSA3qYupsRp8H6BVZKnw2A/RcyqyI9Ez4NTe/H4g","//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","/////////////////////////////////////////////////////////////////////////////8y9YSS+6c2HJ4vLKjzpOahreFwV151rcHaeqWbZp0DD"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0P//////////////////////////////////////gzACCP2yoCgaV9Tu1lsVEeROafCr8HpJY/hw/hekhYK"],"proofs":[{"total":64,"index":7,"leaf_hash":"PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","aunts":["MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":7,"end_row":7},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+r/FKkjvRbxGxvXwctupv6uI5IFwBgRUPyfVjBu9/U84","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+hsPbxDEqMELTQw4ErdPuIirkF7JDrxGdusib5J6RDcB","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+jv/osIC2ya9MWm1KB1ZEAcPn2FXvlRGK3eLDs0Hy2Q/","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+k8pQYudXC9yt6rT1i9JGCHHmcrhGkZ1EfC5OxN5Lf7V","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+tC1eEVsJggh/dwYA7243yudq6J3p51RbOTz6ASaMztJ","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+sPi0F2eW+OWKLWQqplLW3oIFvcgVpUHIq+5ZbNh2RM9","AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/oAAAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+o7JugfzvIzbrj3DNXLcbGQQTejXUe0vX3I8RCVHTdCp"],"subtree_root_proofs":[{"start":8,"end":15,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQyNwFSA3qYupsRp8H6BVZKnw2A/RcyqyI9Ez4NTe/H4g","//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","/////////////////////////////////////////////////////////////////////////////8y9YSS+6c2HJ4vLKjzpOahreFwV151rcHaeqWbZp0DD"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA2pBYmcYY+5ij+g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0P//////////////////////////////////////gzACCP2yoCgaV9Tu1lsVEeROafCr8HpJY/hw/hekhYK"],"proofs":[{"total":64,"index":7,"leaf_hash":"PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","aunts":["MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":7,"end_row":7},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnHkARd/SX2vMVBU6yyZXx+T3Yik8+iDhvSI8orMiDEcz","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnBGHWhNUO+ngy6Mt7KhVojouLImH5+1rBDYoMZttCWJg","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnMBiKYSZ4eAJLDsYXnpu6dFWTuEuD925n/eBpO1JxFVD","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnCppgzXzPHzG/g0W5Rr0I6uPCirisGZ2A3wdHPumopBW","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnJRpSnOShee+AJPwjvlHERpVWatPf9LV3Mj6adjfQcJG","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnPVZqqzK6N2gKiuIxwnbftOqlObI+0X8Cc25Dbvm93dS","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnH4EKphc0X8crJITNgC5N/LRf98Ei/pvFcwpvGZ9Wm+4","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnOICpXdXMsq5jBQEo228b8rfB44XHQZ7CQuXNT4c7Fv9","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnNJ1qmgGJxuIwQfVFzZRaobS9kqJ1LLaX1QboNoZxSmq"],"subtree_root_proofs":[{"start":12,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve7phFZT+bpprud/4w821sV7zS0eigjI61rDF5NAKfX0r","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve48toRnyNefVU5gluwn+IAnYJxfpQF7s+tmGdZNZW27C","/////////////////////////////////////////////////////////////////////////////4hWdR2Ii7EJrAl532ACtqACyRkeqij9FouQ5st+ZfO3"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBCHJrnOXKIZ+yw2LIoFihXk8816ebvy1R1bJkSth7/H+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBD2JRrBniIZ46MX1jwST/OdnwhxXemmob82O9zgOJvz1","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBCOiZE+y14zasHdOPnCTWBmYp9/e+mkaRgbOcPwqxPjS","/////////////////////////////////////////////////////////////////////////////8o5F+WIOhJgz2dHkcbWLOYziGDlhyZ7HW2GWTp1vdDa"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnCEbJz0BNo73xah/NNLONhU6lfAHlfC7VTv2/UYH6GS/","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBO8VsEWRqUJeqJve+4phDxV+Mg1WByBy9jthhdZcPfPF"],"proofs":[{"total":64,"leaf_hash":"yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","aunts":["xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":1,"leaf_hash":"xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","aunts":["yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"end_row":1},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnHkARd/SX2vMVBU6yyZXx+T3Yik8+iDhvSI8orMiDEcz","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnBGHWhNUO+ngy6Mt7KhVojouLImH5+1rBDYoMZttCWJg","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnMBiKYSZ4eAJLDsYXnpu6dFWTuEuD925n/eBpO1JxFVD","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnCppgzXzPHzG/g0W5Rr0I6uPCirisGZ2A3wdHPumopBW","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnJRpSnOShee+AJPwjvlHERpVWatPf9LV3Mj6adjfQcJG","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnPVZqqzK6N2gKiuIxwnbftOqlObI+0X8Cc25Dbvm93dS","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnH4EKphc0X8crJITNgC5N/LRf98Ei/pvFcwpvGZ9Wm+4","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnOICpXdXMsq5jBQEo228b8rfB44XHQZ7CQuXNT4c7Fv9","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnNJ1qmgGJxuIwQfVFzZRaobS9kqJ1LLaX1QboNoZxSmq"],"subtree_root_proofs":[{"start":12,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve7phFZT+bpprud/4w821sV7zS0eigjI61rDF5NAKfX0r","AAAAAAAAAAAAAAAAAAAAAAAAAA6Qs3XiPR33L3sAAAAAAAAAAAAAAAAAAAAAAAAADpCzdeI9Hfcve48toRnyNefVU5gluwn+IAnYJxfpQF7s+tmGdZNZW27C","/////////////////////////////////////////////////////////////////////////////4hWdR2Ii7EJrAl532ACtqACyRkeqij9FouQ5st+ZfO3"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBCHJrnOXKIZ+yw2LIoFihXk8816ebvy1R1bJkSth7/H+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBD2JRrBniIZ46MX1jwST/OdnwhxXemmob82O9zgOJvz1","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBCOiZE+y14zasHdOPnCTWBmYp9/e+mkaRgbOcPwqxPjS","/////////////////////////////////////////////////////////////////////////////8o5F+WIOhJgz2dHkcbWLOYziGDlhyZ7HW2GWTp1vdDa"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnCEbJz0BNo73xah/NNLONhU6lfAHlfC7VTv2/UYH6GS/","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBO8VsEWRqUJeqJve+4phDxV+Mg1WByBy9jthhdZcPfPF"],"proofs":[{"total":64,"leaf_hash":"yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","aunts":["xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":1,"leaf_hash":"xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","aunts":["yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"end_row":1},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3PUF/XkCT50/rgbVPQ+6i+6uLs7VTNdK0hXlBiDccgu","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DxtNscYvzRmrQnqn6n5FhYDgzl1hhGgL3gXmebT3MHY3","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D5Sd/36RToS4zoioBPtPbZOePfQwC6A4uCiXC141lNmN","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D6CZxI/TJZOeBbiAC1m/6izj0jskmz5LycSNLHuZ5gjr","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D96dybT+inP7u3MWqU06PBwStucNb+QLUQUOuZVBBp3j","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3A+NQKcbZ9Y2Kd+70HvSgX5+iwUjNN7lZi+njZGkqHV","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D00TRFKvrLcmaBotVZ0tp2wLl2vpjReFyrsolVs1s0Ja","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D16W3N3pM4LelBJQ2Nd1TR1MSJaBG4zdNwoky+oTVhCo","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D9nS8s2jjcMF8BuQK9aMSCWomO8mtoyHADTM8FlWX5K8","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2Dy0hPLB2rRBhqfq/tsnxd3N3WSn95qeIw4Elbyujm1E0","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D1u9uiY3ferIKzSkoJ7ReH3Dc/aT3W1XYN1AM/INJjbS"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML5wbK1i4+xP0AzGCwa4dgHVjpWTl6oJi6MG9dNoZD/scf","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML56gkF9T0uVub+AiXhRPoWxLZxY0+X1UM3JfwOzYRbbfR","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML55maaZYt5MWU9VvjAUtNNVyJeRXDLm9kd/W4SWkPO5sX","/////////////////////////////////////////////////////////////////////////////3zchrSpIq7t1zoZyklgN8S5i4JnRrkoV0Lq4ZwY8NM8"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52phkHJZtSTGdPBvRfatD6p7G8mtJ69grgxnAwlBtYygP","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5kJqEC1tLdMnaJvNCxrqzWhHgGVkrVDd44b0520UK5n7","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5iRaSxDzlg6Mtq0+LgIBNasiOTaeQcnqGoHoIw2vyN2c","/////////////////////////////////////////////////////////////////////////////5wcCTpZvE0S4+peb+IZFHDHaPdqDosLK4qwh+wextWG"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2Dw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DxRGOBtYJt/tMg258Uli/Ksu3lFF29XERn0eyu3jTpgP","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5pHVTsd/j4nwBLfYA9TfmtRjGi771Yki9q31nstfVQJu"],"proofs":[{"total":64,"index":3,"leaf_hash":"/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","aunts":["kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":4,"leaf_hash":"HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","aunts":["o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":3,"end_row":4},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3PUF/XkCT50/rgbVPQ+6i+6uLs7VTNdK0hXlBiDccgu","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DxtNscYvzRmrQnqn6n5FhYDgzl1hhGgL3gXmebT3MHY3","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D5Sd/36RToS4zoioBPtPbZOePfQwC6A4uCiXC141lNmN","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D6CZxI/TJZOeBbiAC1m/6izj0jskmz5LycSNLHuZ5gjr","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D96dybT+inP7u3MWqU06PBwStucNb+QLUQUOuZVBBp3j","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3A+NQKcbZ9Y2Kd+70HvSgX5+iwUjNN7lZi+njZGkqHV","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D00TRFKvrLcmaBotVZ0tp2wLl2vpjReFyrsolVs1s0Ja","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D16W3N3pM4LelBJQ2Nd1TR1MSJaBG4zdNwoky+oTVhCo","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D9nS8s2jjcMF8BuQK9aMSCWomO8mtoyHADTM8FlWX5K8","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2Dy0hPLB2rRBhqfq/tsnxd3N3WSn95qeIw4Elbyujm1E0","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D1u9uiY3ferIKzSkoJ7ReH3Dc/aT3W1XYN1AM/INJjbS"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML5wbK1i4+xP0AzGCwa4dgHVjpWTl6oJi6MG9dNoZD/scf","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML56gkF9T0uVub+AiXhRPoWxLZxY0+X1UM3JfwOzYRbbfR","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML55maaZYt5MWU9VvjAUtNNVyJeRXDLm9kd/W4SWkPO5sX","/////////////////////////////////////////////////////////////////////////////3zchrSpIq7t1zoZyklgN8S5i4JnRrkoV0Lq4ZwY8NM8"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52phkHJZtSTGdPBvRfatD6p7G8mtJ69grgxnAwlBtYygP","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5kJqEC1tLdMnaJvNCxrqzWhHgGVkrVDd44b0520UK5n7","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5iRaSxDzlg6Mtq0+LgIBNasiOTaeQcnqGoHoIw2vyN2c","/////////////////////////////////////////////////////////////////////////////5wcCTpZvE0S4+peb+IZFHDHaPdqDosLK4qwh+wextWG"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2Dw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DxRGOBtYJt/tMg258Uli/Ksu3lFF29XERn0eyu3jTpgP","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5pHVTsd/j4nwBLfYA9TfmtRjGi771Yki9q31nstfVQJu"],"proofs":[{"total":64,"index":3,"leaf_hash":"/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","aunts":["kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":4,"leaf_hash":"HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","aunts":["o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":3,"end_row":4},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5vfo3RPSbjeXfY4UtRaGOpxtxcCl9Plvp+vz0VHI33g7","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5uS4afGoQymu1jDQiz09WjN00ipuks8iInNZa+ZCXX73","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5gp//GDfhF99FbJN4zNlcMrefQgsufelQckfVk09SDbF","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5iDNLf/LAPx/jJ7Z0lGpmzt99yQQA9lypiDUONTZr0Mk","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5sT/9UpIKDybxU6dnZiMCG6ux2xDj669ABWtQoyqPuBY","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5vn9YP5SuSU6PBGofJsfG7st3FG6Qixph3iTA9Orz5RC","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5juyznmkQoyrydR6C2niZqMawd8l12IGSHNNQ8Xabq8A","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5nUVBwG4dO/pmxyjOlCBm5UU/vEFVZQDRbSz35S+klzp","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5qzwdUcqjwBZgRx1+vOaZ6t942e99NSs/wB3sTYRFg/a","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5lEHZr3YpG7NdJAc9XYaUXSuR8tDwtSK5QPUq/Gv8ZED","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5l3H/LH529FHX+uXJOg4rbSsFCayFipIvFiVr7M/MU9/","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5uGB1YQPzoWJ6JNeobtUGtcUIlOxBHJJeGPe5+OZgBwO","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5mS7Z60l4kE5cR7dMcC4v33Nbwbhm44wQqCApjSbomC2"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52poXHRSocIjgFC3HqQwYtlHpTnLPbjmbL/VNp+jrEME2","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52g+d0EwnW6jMox0p2njI0Sq/lGhAwZU3db7s4ukdtQ4P","/////////////////////////////////////////////////////////////////////////////5wcCTpZvE0S4+peb+IZFHDHaPdqDosLK4qwh+wextWG"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT86T1wTqdrlg+bhwj/bEyy1YOwf93FP2zWhM7lA68H7sj","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8wX/+oZ33fhdu2AQKX+sUsMqPkpe/MaXS60q5dAR3MlN","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT82Dom0BSkUjV6sMubSq30ALDic1WXWbCPAQndt1cN00T","/////////////////////////////////////////////////////////////////////////////y83ef4glBrF718lZv+hrtMRtCfHwI+2Uzm6dpAqr+ZL"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5pHVTsd/j4nwBLfYA9TfmtRjGi771Yki9q31nstfVQJu","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT87PDW5hrJ5RVxdQQxw1aGqQGh2WFfJhphqodE30dPRJu"],"proofs":[{"total":64,"index":4,"leaf_hash":"HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","aunts":["o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":5,"leaf_hash":"o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","aunts":["HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":4,"end_row":5},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5vfo3RPSbjeXfY4UtRaGOpxtxcCl9Plvp+vz0VHI33g7","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5uS4afGoQymu1jDQiz09WjN00ipuks8iInNZa+ZCXX73","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5gp//GDfhF99FbJN4zNlcMrefQgsufelQckfVk09SDbF","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5iDNLf/LAPx/jJ7Z0lGpmzt99yQQA9lypiDUONTZr0Mk","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5sT/9UpIKDybxU6dnZiMCG6ux2xDj669ABWtQoyqPuBY","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5vn9YP5SuSU6PBGofJsfG7st3FG6Qixph3iTA9Orz5RC","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5juyznmkQoyrydR6C2niZqMawd8l12IGSHNNQ8Xabq8A","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5nUVBwG4dO/pmxyjOlCBm5UU/vEFVZQDRbSz35S+klzp","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5qzwdUcqjwBZgRx1+vOaZ6t942e99NSs/wB3sTYRFg/a","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5lEHZr3YpG7NdJAc9XYaUXSuR8tDwtSK5QPUq/Gv8ZED","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5l3H/LH529FHX+uXJOg4rbSsFCayFipIvFiVr7M/MU9/","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5uGB1YQPzoWJ6JNeobtUGtcUIlOxBHJJeGPe5+OZgBwO","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5mS7Z60l4kE5cR7dMcC4v33Nbwbhm44wQqCApjSbomC2"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52poXHRSocIjgFC3HqQwYtlHpTnLPbjmbL/VNp+jrEME2","AAAAAAAAAAAAAAAAAAAAAAAAAIhX1JZfpht3+doAAAAAAAAAAAAAAAAAAAAAAAAAiFfUll+mG3f52g+d0EwnW6jMox0p2njI0Sq/lGhAwZU3db7s4ukdtQ4P","/////////////////////////////////////////////////////////////////////////////5wcCTpZvE0S4+peb+IZFHDHaPdqDosLK4qwh+wextWG"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT86T1wTqdrlg+bhwj/bEyy1YOwf93FP2zWhM7lA68H7sj","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8wX/+oZ33fhdu2AQKX+sUsMqPkpe/MaXS60q5dAR3MlN","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT82Dom0BSkUjV6sMubSq30ALDic1WXWbCPAQndt1cN00T","/////////////////////////////////////////////////////////////////////////////y83ef4glBrF718lZv+hrtMRtCfHwI+2Uzm6dpAqr+ZL"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5pHVTsd/j4nwBLfYA9TfmtRjGi771Yki9q31nstfVQJu","AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT87PDW5hrJ5RVxdQQxw1aGqQGh2WFfJhphqodE30dPRJu"],"proofs":[{"total":64,"index":4,"leaf_hash":"HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","aunts":["o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":5,"leaf_hash":"o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","aunts":["HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":4,"end_row":5},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBCHJrnOXKIZ+yw2LIoFihXk8816ebvy1R1bJkSth7/H+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBBNbksKk6MrezNKMfiXHPbGIk2gvvc3HOqh4FCM69G+H","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBMSOryKoOr3d+gyFA6E4apiMHdiXgfhHoTZbxv6/8GY3","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBKUvMOhDqtFuCq9bwN7j2yt/Q3nCmDN222bb4CA2bP7m","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBMSXtVPHgjuk7ZDswK2Xbqti23dOJL27JI44RYqplzyI","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBAnklIMPJAcGFt/aL6qxNVyE3nJswdweomJQtGg8qmc6","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBBkcAsSSFvn7lHt8oQjdHkc622+0QH96/kMzILgIiAF+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBDYaTdgj0pUCH1WEjY3XKfp0fZriNiLZ/UeY9QEPz3b9","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBFqddXvbtUl7AMRhqNbgeufceuu3N15XfA8VKED4EJaD","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBIIvL0yRi2CGuypAA4jEs9nuZN10r5KZxI3NQdbB7BR+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBI6lw8RWYJ2wp4k/dJQFNuZaSfEpu40/W2Fc9eAR9UWg","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBK4oPeMAcr7H1kDcLbn8UdwuRu4u4kRqZho9l4f+FQlE","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBFO19zbxZQo+Mxizt0Yt9s98mi8h7Q3lcpo70YpKU0DF","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBOls5qzT0t2dqKIBT42T9SRgGvxhgznEuilq01wBDLnd","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBOw+qr8QGuaCY77CpdyDj1FIPq3zLgQxxc59+mCCeVl9"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnMzTsnVddBSsn03Zjf/HFazEJLU/626L6iQF+53Qc3Lg","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnNJ1qmgGJxuIwQfVFzZRaobS9kqJ1LLaX1QboNoZxSmq","/////////////////////////////////////////////////////////////////////////////8o5F+WIOhJgz2dHkcbWLOYziGDlhyZ7HW2GWTp1vdDa"],"is_max_namespace_ignored":true},{"end":4,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML583zPplKsahVhB6zIwCpTNxKNax1tE+h/VSquHowBAbd","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML52BTxo5jeqOZ2+oOGHDUGU/kBszfpmiXIyzZzceIvBF3","/////////////////////////////////////////////////////////////////////////////1Ae1fblLYf8dEBTGML3GacmoIn2ULVkvabn+bEtKx2h"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBO8VsEWRqUJeqJve+4phDxV+Mg1WByBy9jthhdZcPfPF","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML500N1PccFReLF4QSHb58qPxpEzdhZAy2rm8B3ljn1xlX"],"proofs":[{"total":64,"index":1,"leaf_hash":"xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","aunts":["yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":2,"leaf_hash":"kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","aunts":["/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":1,"end_row":2},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBCHJrnOXKIZ+yw2LIoFihXk8816ebvy1R1bJkSth7/H+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBBNbksKk6MrezNKMfiXHPbGIk2gvvc3HOqh4FCM69G+H","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBMSOryKoOr3d+gyFA6E4apiMHdiXgfhHoTZbxv6/8GY3","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBKUvMOhDqtFuCq9bwN7j2yt/Q3nCmDN222bb4CA2bP7m","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBMSXtVPHgjuk7ZDswK2Xbqti23dOJL27JI44RYqplzyI","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBAnklIMPJAcGFt/aL6qxNVyE3nJswdweomJQtGg8qmc6","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBBkcAsSSFvn7lHt8oQjdHkc622+0QH96/kMzILgIiAF+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBDYaTdgj0pUCH1WEjY3XKfp0fZriNiLZ/UeY9QEPz3b9","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBFqddXvbtUl7AMRhqNbgeufceuu3N15XfA8VKED4EJaD","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBIIvL0yRi2CGuypAA4jEs9nuZN10r5KZxI3NQdbB7BR+","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBI6lw8RWYJ2wp4k/dJQFNuZaSfEpu40/W2Fc9eAR9UWg","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBK4oPeMAcr7H1kDcLbn8UdwuRu4u4kRqZho9l4f+FQlE","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBFO19zbxZQo+Mxizt0Yt9s98mi8h7Q3lcpo70YpKU0DF","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBOls5qzT0t2dqKIBT42T9SRgGvxhgznEuilq01wBDLnd","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBOw+qr8QGuaCY77CpdyDj1FIPq3zLgQxxc59+mCCeVl9"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnMzTsnVddBSsn03Zjf/HFazEJLU/626L6iQF+53Qc3Lg","AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAEMJBW9Ks0+5cnNJ1qmgGJxuIwQfVFzZRaobS9kqJ1LLaX1QboNoZxSmq","/////////////////////////////////////////////////////////////////////////////8o5F+WIOhJgz2dHkcbWLOYziGDlhyZ7HW2GWTp1vdDa"],"is_max_namespace_ignored":true},{"end":4,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML583zPplKsahVhB6zIwCpTNxKNax1tE+h/VSquHowBAbd","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML52BTxo5jeqOZ2+oOGHDUGU/kBszfpmiXIyzZzceIvBF3","/////////////////////////////////////////////////////////////////////////////1Ae1fblLYf8dEBTGML3GacmoIn2ULVkvabn+bEtKx2h"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABDCQVvSrNPuXJwAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBO8VsEWRqUJeqJve+4phDxV+Mg1WByBy9jthhdZcPfPF","AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML500N1PccFReLF4QSHb58qPxpEzdhZAy2rm8B3ljn1xlX"],"proofs":[{"total":64,"index":1,"leaf_hash":"xZ0djL9n2fIY3ctvCWNmacPTItYELvjXmFdPm1Ns6rM=","aunts":["yPqwKs3mEJBGav9UkZLupbIbRFxM/jFYq3H3K6EDJ+E=","jmZOqgojYBWHhJPdkGfZajn9/oVuHtSFfcgbceoJ5t8=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":2,"leaf_hash":"kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","aunts":["/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":1,"end_row":2},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8wr3XHqB7yBO+RlA0/I+paFz3lDBoLDDwCuc3EH8/rqu","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8zm8kX4w200dwCz+SBFe4QYOrR2uAigBgvEkhCKEsOv3","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8zLh69mNwQaCm4hoonDI8lG6EcW8a/Z5fMcReTLc8D9C","/////////////////////////////////////////////////////////////////////////////1G7p2UHfed2ZwUssvkyeePJIRwy/qfHVVmTW/sAdPyR"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/r//////////////////////////////////////jA8RXIA3egwBsAkmOwpmZpM5cH9mghujrDXktkZNM/M","/////////////////////////////////////////////////////////////////////////////8y9YSS+6c2HJ4vLKjzpOahreFwV151rcHaeqWbZp0DD"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ48JXjdVlyhMrqBJ99TY9SviBAdE0rX0Cumr415AT447","AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0P//////////////////////////////////////gzACCP2yoCgaV9Tu1lsVEeROafCr8HpJY/hw/hekhYK"],"proofs":[{"total":64,"index":6,"leaf_hash":"MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","aunts":["PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":7,"leaf_hash":"PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","aunts":["MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":6,"end_row":7},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8wr3XHqB7yBO+RlA0/I+paFz3lDBoLDDwCuc3EH8/rqu","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8zm8kX4w200dwCz+SBFe4QYOrR2uAigBgvEkhCKEsOv3","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8zLh69mNwQaCm4hoonDI8lG6EcW8a/Z5fMcReTLc8D9C","/////////////////////////////////////////////////////////////////////////////1G7p2UHfed2ZwUssvkyeePJIRwy/qfHVVmTW/sAdPyR"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAANqQWJnGGPuYo/r//////////////////////////////////////jA8RXIA3egwBsAkmOwpmZpM5cH9mghujrDXktkZNM/M","/////////////////////////////////////////////////////////////////////////////8y9YSS+6c2HJ4vLKjzpOahreFwV151rcHaeqWbZp0DD"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ48JXjdVlyhMrqBJ99TY9SviBAdE0rX0Cumr415AT447","AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0P//////////////////////////////////////gzACCP2yoCgaV9Tu1lsVEeROafCr8HpJY/hw/hekhYK"],"proofs":[{"total":64,"index":6,"leaf_hash":"MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","aunts":["PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":7,"leaf_hash":"PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","aunts":["MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":6,"end_row":7},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":4,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBJf15E3DFS/K7yuQA26KGSzn5Ho2T3pyngP0xT1IDhh+","/////////////////////////////////////////////////////////////////////////////1Ae1fblLYf8dEBTGML3GacmoIn2ULVkvabn+bEtKx2h"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3PUF/XkCT50/rgbVPQ+6i+6uLs7VTNdK0hXlBiDccgu","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DzW65cmGmXIZz9WvAEwAOq4+TmR5Txo8rfUoPhMsb60c","/////////////////////////////////////////////////////////////////////////////3zchrSpIq7t1zoZyklgN8S5i4JnRrkoV0Lq4ZwY8NM8"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML5w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML500N1PccFReLF4QSHb58qPxpEzdhZAy2rm8B3ljn1xlX","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DxRGOBtYJt/tMg258Uli/Ksu3lFF29XERn0eyu3jTpgP"],"proofs":[{"total":64,"index":2,"leaf_hash":"kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","aunts":["/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":3,"leaf_hash":"/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","aunts":["kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":2,"end_row":3},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":4,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAPDXT+CkaJwsXBJf15E3DFS/K7yuQA26KGSzn5Ho2T3pyngP0xT1IDhh+","/////////////////////////////////////////////////////////////////////////////1Ae1fblLYf8dEBTGML3GacmoIn2ULVkvabn+bEtKx2h"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2D3PUF/XkCT50/rgbVPQ+6i+6uLs7VTNdK0hXlBiDccgu","AAAAAAAAAAAAAAAAAAAAAAAAAIKLFSSWry1Gdg8AAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DzW65cmGmXIZz9WvAEwAOq4+TmR5Txo8rfUoPhMsb60c","/////////////////////////////////////////////////////////////////////////////3zchrSpIq7t1zoZyklgN8S5i4JnRrkoV0Lq4ZwY8NM8"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML5w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADw10/gpGicLFwQAAAAAAAAAAAAAAAAAAAAAAAAAdSoIYCrVTrML500N1PccFReLF4QSHb58qPxpEzdhZAy2rm8B3ljn1xlX","AAAAAAAAAAAAAAAAAAAAAAAAAHUqCGAq1U6zC+cAAAAAAAAAAAAAAAAAAAAAAAAAgosVJJavLUZ2DxRGOBtYJt/tMg258Uli/Ksu3lFF29XERn0eyu3jTpgP"],"proofs":[{"total":64,"index":2,"leaf_hash":"kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","aunts":["/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":3,"leaf_hash":"/hNkI5vz6zlPnAVNmcxImF8DrN864eMICdEozX27ayc=","aunts":["kDrwyCL/6Sx6KSJFbwZQ+/jqy056Hinmvk/4nayMOn8=","Z5/yInQxaOMy4/J8iIhp/0QD9WXGNpFAAadtN4Uco2Y=","Yip1iB35sCJV+rpf/NZIEpnCzOYYrv9BdzCOnvxqHNE=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":2,"end_row":3},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT89KSlDlsJ+ReZNt3v/a2xlpMe3qswY1TbNFRVA0WMLsn","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT86XzpG4SIyIzmV6UKvLzepyCN9w8lnmOFBEWoLfJHwhB","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT84jWglnX/BdDlI+/5Jic6y0w0LpY0iPNefCTaTfaIldA","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT85aXBTYCM1NXNndk6kibEaXKdkNwni5Admaf9kn6MrF5","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT81FbRgWosQadPezMxy2aU7BfSkD3dJ0Eh74DDaPCI5Cm","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT813HX17y/KaJB3LkL3fxWG+1PkVYwPsMkcgLHYywbuRo","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8wQfQPuv5OoX0+n0UD1K2DSpWx52XdSgzCU1zjp6CpSn","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT80UMRhLkvL778OLElYYQQ9k0fkemSeF73CxGHZHA9iqn","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT85QHxoLO3qWbF4eWec59ShnuZaWFXiDPMt1RQa4kQXMe","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT83fZng232MX0CQYVMmlYAi2+v2UJpGD2pSO3Fdm4s62+","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT83jTe4DC4MHJxK7oJNoylZ+uaeIP08gqb6+63Jffmlmu","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT85fryggzyTPza8GdLGBuAovYMXHRIlzO88mcdpHJIJ1I","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8yDYWnlU4WN6eqh5hn57rAOdReVEJm7AmJDvFmHG3aSZ","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT84dxP2tptA4wxEuUe1H4/929YBITMdlRGuZf7ZKnU3VQ","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8+fOWs/EDtm04isDTUle6WkdYqqz9/+fWe65PJkQT5Rn","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT80g5mSY1MkF4ivOvrcFajdCMzsVj/NHfr5xQWzuyUOwI","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8w9kjNGskHHtCE7DTTbckAW0yjFmuFkVZx/Ss0GMhpZZ","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8wDnrjysMr5HZWCqiUmck8vb5oGMZmSM4MrevTUqieXE","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT813fCkmZeUsfhKKpvbhTGKRbRCjGN8s5PLu2PDSF/wLZ","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT84lAj8XDbdzIAdW0SJJJ9tBiZy6YpKthJdEOGGfp1cs2","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8zLh69mNwQaCm4hoonDI8lG6EcW8a/Z5fMcReTLc8D9C"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5hS0Xshu2GXnDzHlt50eHUCuLPnUso38V3qxkBYFSDwn","/////////////////////////////////////////////////////////////////////////////y83ef4glBrF718lZv+hrtMRtCfHwI+2Uzm6dpAqr+ZL"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ6ZDmksSyCbl4enEFVWDNV7/ctHN1dICRAE2dFqV6GM/","AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ+3vSRXcJAuEx/WM1/ae+vNDF0g5bPXLTpgLe8yzTULz","/////////////////////////////////////////////////////////////////////////////1G7p2UHfed2ZwUssvkyeePJIRwy/qfHVVmTW/sAdPyR"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT87PDW5hrJ5RVxdQQxw1aGqQGh2WFfJhphqodE30dPRJu","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ48JXjdVlyhMrqBJ99TY9SviBAdE0rX0Cumr415AT447"],"proofs":[{"total":64,"index":5,"leaf_hash":"o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","aunts":["HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":6,"leaf_hash":"MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","aunts":["PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":5,"end_row":6},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAwWlFO3siJTGI5hS0Xshu2GXnDzHlt50eHUCuLPnUso38V3qxkBYFSDwn","/////////////////////////////////////////////////////////////////////////////y83ef4glBrF718lZv+hrtMRtCfHwI+2Uzm6dpAqr+ZL"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ6ZDmksSyCbl4enEFVWDNV7/ctHN1dICRAE2dFqV6GM/","AAAAAAAAAAAAAAAAAAAAAAAAAM+4HbxCZ+aEj0MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ+3vSRXcJAuEx/WM1/ae+vNDF0g5bPXLTpgLe8yzTULz","/////////////////////////////////////////////////////////////////////////////1G7p2UHfed2ZwUssvkyeePJIRwy/qfHVVmTW/sAdPyR"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT8w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMFpRTt7IiUxiOYAAAAAAAAAAAAAAAAAAAAAAAAAzD8oE/elsKuT87PDW5hrJ5RVxdQQxw1aGqQGh2WFfJhphqodE30dPRJu","AAAAAAAAAAAAAAAAAAAAAAAAAMw/KBP3pbCrk/MAAAAAAAAAAAAAAAAAAAAAAAAAz7gdvEJn5oSPQ48JXjdVlyhMrqBJ99TY9SviBAdE0rX0Cumr415AT447"],"proofs":[{"total":64,"index":5,"leaf_hash":"o2E7+mP19V3dCWZwP+aAWcDS2UpKeJScp4lFQ6UCNJ8=","aunts":["HaAgi5nSayGw9kdUE/UwmeG1crz/Qrfxwtcja6GN8aY=","PtUiCTkgdiu6JTcM0WSicBJwKHZpomjt89jPQW+tOCM=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]},{"total":64,"index":6,"leaf_hash":"MB9yY90KmVyBGz0bN6W+j9C3lNmiDaUuu0fILFSen78=","aunts":["PTsTSQLniQdUfGKiuUNEZ+3QSrKFstYgDqy1bw+K9yg=","m8oMknYBd9sx9JgS76ek+Lv+aqJch4YEFH4+s0OsIwk=","/ZoRKycV4XmYL/aLxxvMO1Ki8C+FUwBqTfxjC5jVSIU=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","M2a5FIM1LEvF8/ztDfQ3FCbsTII4pNzgfeSxdQITNQM=","8iAVEy3dKUoyEP7E2OzWMv728qY/jBM0lo65A9Pv7q0="]}],"start_row":5,"end_row":6},"namespace_version":0},"root":"cZDElfVCt1Suu4Q6vzPFHveo1htXWs0CXSEeN1/1ki0=","sub_threshold":64}] \ No newline at end of file diff --git a/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65206c657373207468616e2061207368617265.json b/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65206c657373207468616e2061207368617265.json new file mode 100755 index 0000000000..e3f51c33dc --- /dev/null +++ b/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65206c657373207468616e2061207368617265.json @@ -0,0 +1 @@ +[{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAB7FBOK48WeyQQMAAAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBA85yPkbe1uGziktblgdE1gymy7yX47yH4RZU8i77BlNJ"],"subtree_root_proofs":[{"start":7,"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww0aB3/p7XliWs6PSnD3Gg0xJypf6MRKvY6MOY9GYsyn6","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww4l5lwvscWt5oROj9I2RDqf+YE+Qc5yFPu3kaGtfvdFQ","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww9Fes/YqjhPxbGB0cb7YLBD3LWGFh51AUQsaac7i0IiA","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0nS1Z999VJ8Vr7apR9kWg+6i2WfwSqzt2/ASX1Dwac3c","//////////////////////////////////////////////////////////////////////////////RZ4DIjyeS33lzPqKfHa/fwfqhFKjA2JqDPkq53gZFS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBAw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0loLmE64NxCl3hnvl0FiRrM318oa6gvJ2ZXIQHKxT9b+"],"proofs":[{"total":64,"index":1,"leaf_hash":"JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","aunts":["LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":1,"end_row":1},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAB7FBOK48WeyQQMAAAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBA85yPkbe1uGziktblgdE1gymy7yX47yH4RZU8i77BlNJ"],"subtree_root_proofs":[{"start":7,"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww0aB3/p7XliWs6PSnD3Gg0xJypf6MRKvY6MOY9GYsyn6","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww4l5lwvscWt5oROj9I2RDqf+YE+Qc5yFPu3kaGtfvdFQ","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww9Fes/YqjhPxbGB0cb7YLBD3LWGFh51AUQsaac7i0IiA","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0nS1Z999VJ8Vr7apR9kWg+6i2WfwSqzt2/ASX1Dwac3c","//////////////////////////////////////////////////////////////////////////////RZ4DIjyeS33lzPqKfHa/fwfqhFKjA2JqDPkq53gZFS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBAw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0loLmE64NxCl3hnvl0FiRrM318oa6gvJ2ZXIQHKxT9b+"],"proofs":[{"total":64,"index":1,"leaf_hash":"JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","aunts":["LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":1,"end_row":1},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFLCNwKxDz1gCdPywaNYzoj6E0sIazBS6tmhIq5iX+MDO","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFLPvezmk8wYUpqBhpk4ZwQ/sP0y8IEGkFgwEz9ughaC1","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFBDteKQbcxJBcmqEXWlQIUEd5+2W1vDyZFjsDyhuIbYY"],"subtree_root_proofs":[{"start":4,"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0jlE/0U9NX0oxdKVYpG9hTOksEh3JfzgdNeO1Y7sp/d+","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyv9a1y1NsjyqYy2awZN5droMpDLJLY2Qwr2SeCR141Pc","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUykr/RQCaggUWRpoUd7UC5bqRUkN+pZyDTOgBCqgu+4mU","/////////////////////////////////////////////////////////////////////////////ztJEbF7NawT8FbhPGOZU654FgVnxch9gF900JBMFcSI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyoIuCk7uuLaeGpMbJBTmPKJTs1FUXHhvwVRNmnsQW4eM"],"proofs":[{"total":64,"index":2,"leaf_hash":"ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","aunts":["ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":2,"end_row":2},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFLCNwKxDz1gCdPywaNYzoj6E0sIazBS6tmhIq5iX+MDO","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFLPvezmk8wYUpqBhpk4ZwQ/sP0y8IEGkFgwEz9ughaC1","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFBDteKQbcxJBcmqEXWlQIUEd5+2W1vDyZFjsDyhuIbYY"],"subtree_root_proofs":[{"start":4,"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0jlE/0U9NX0oxdKVYpG9hTOksEh3JfzgdNeO1Y7sp/d+","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyv9a1y1NsjyqYy2awZN5droMpDLJLY2Qwr2SeCR141Pc","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUykr/RQCaggUWRpoUd7UC5bqRUkN+pZyDTOgBCqgu+4mU","/////////////////////////////////////////////////////////////////////////////ztJEbF7NawT8FbhPGOZU654FgVnxch9gF900JBMFcSI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyoIuCk7uuLaeGpMbJBTmPKJTs1FUXHhvwVRNmnsQW4eM"],"proofs":[{"total":64,"index":2,"leaf_hash":"ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","aunts":["ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":2,"end_row":2},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+E0YhqM1mPlsAnBXYqKyrHOqigsP/U1HFCO3LzAKbzdN","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+MVQutCgIiARKXDDkGLV5Ea2pART95L3UEatAYJqtAXs","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+O5XBnExNLfA6NUwxt/RwwBBnn9c4sX/EtEu3R5Twd9q","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+INTsBocWccKlPNqu7dYLyy+u83wIOhcxlBgPykX1Z66","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+IARjFnvV81wfPQ7mprQNhVdCLWvmBVDr3rEQ4bSjb5z"],"subtree_root_proofs":[{"start":9,"end":14,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO8HNbaVEeRM+O1hBay7wGwdK8gfgw1UcN/2rlcvXjPTG","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO3HuI/5EN6ZOUMCBBv1vi3c+t/y5CA8tyKkljtQXn6fN","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124tSE6eJb3UFfQjDmgJqVX+eSNkf0M/DS0HUyceLjTfJ1","/////////////////////////////////////////////////////////////////////////////z6e4gxrnePPmnNXFAh0C4Hxag4WE2jEwAaC0kcrgulI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124h/mJb8eSc8itnPyFz5gI4KFyniTBfsMsZjCcry5Ym6a"],"proofs":[{"total":64,"index":4,"leaf_hash":"dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","aunts":["iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":4,"end_row":4},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+E0YhqM1mPlsAnBXYqKyrHOqigsP/U1HFCO3LzAKbzdN","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+MVQutCgIiARKXDDkGLV5Ea2pART95L3UEatAYJqtAXs","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+O5XBnExNLfA6NUwxt/RwwBBnn9c4sX/EtEu3R5Twd9q","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+INTsBocWccKlPNqu7dYLyy+u83wIOhcxlBgPykX1Z66","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+IARjFnvV81wfPQ7mprQNhVdCLWvmBVDr3rEQ4bSjb5z"],"subtree_root_proofs":[{"start":9,"end":14,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO8HNbaVEeRM+O1hBay7wGwdK8gfgw1UcN/2rlcvXjPTG","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO3HuI/5EN6ZOUMCBBv1vi3c+t/y5CA8tyKkljtQXn6fN","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124tSE6eJb3UFfQjDmgJqVX+eSNkf0M/DS0HUyceLjTfJ1","/////////////////////////////////////////////////////////////////////////////z6e4gxrnePPmnNXFAh0C4Hxag4WE2jEwAaC0kcrgulI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124h/mJb8eSc8itnPyFz5gI4KFyniTBfsMsZjCcry5Ym6a"],"proofs":[{"total":64,"index":4,"leaf_hash":"dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","aunts":["iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":4,"end_row":4},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124phrIX39zIY8Abrj1kHosQq+nz8YMvXcKHVqPNR+NVHg","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124vdPSHgntetPAFN5rRGTGEzFJmsm8B97R2R5b/fh+pzZ","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124pirmxQyB/rndCshDJZ8hTfZtw+UkPNiWV8owzqZauD/","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124v4vp9dP+zFSrfBLhTveBYU8Qr+t/C9POStw+gtBYl4F","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124mHalziH0KYFO0keOhyfudJhgqmgKNf1Pz3S3vrnO+Cz","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124mOzRWUD495WNM7KIWLy6AmWWFzy8KPfpmp2hDJJ+rVJ","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124njeEtsLquJQ+2srPU3lz1FlRQ+gO17GYXFqoIjUFKq5"],"subtree_root_proofs":[{"start":14,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO8HNbaVEeRM+O1hBay7wGwdK8gfgw1UcN/2rlcvXjPTG","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+BYgIVspgB8oPRgn31f4UauroWLZd+Ml719urDeXx8+9","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+MGRBAZRg44q3VzQFeFqRYI+761+jGDc597Rnk4dvb3V","/////////////////////////////////////////////////////////////////////////////z6e4gxrnePPmnNXFAh0C4Hxag4WE2jEwAaC0kcrgulI"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckNvlIOuKAUPBTuG/2d9MfN/AaiwY71idaZdrMmq0NjAN","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckAT6xT/mkIu7EdOcqHT6kPjN57xvL8thnqlgmdPK2q8p","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckOS7vgw+OBNt3IxgdhJchLhARF3dX8aiFCnteBpdvqG/","/////////////////////////////////////////////////////////////////////////////5eAoa+9+6XecR94lkNN2ZLRP3cinm4Y6YG3YOV+/0jF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124h/mJb8eSc8itnPyFz5gI4KFyniTBfsMsZjCcry5Ym6a","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckK8NvBLXCIp/8g18echPjYjOpZbJb33wzQjwVATQ8SC/"],"proofs":[{"total":64,"index":4,"leaf_hash":"dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","aunts":["iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":5,"leaf_hash":"iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","aunts":["dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":4,"end_row":5},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124phrIX39zIY8Abrj1kHosQq+nz8YMvXcKHVqPNR+NVHg","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124vdPSHgntetPAFN5rRGTGEzFJmsm8B97R2R5b/fh+pzZ","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124pirmxQyB/rndCshDJZ8hTfZtw+UkPNiWV8owzqZauD/","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124v4vp9dP+zFSrfBLhTveBYU8Qr+t/C9POStw+gtBYl4F","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124mHalziH0KYFO0keOhyfudJhgqmgKNf1Pz3S3vrnO+Cz","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124mOzRWUD495WNM7KIWLy6AmWWFzy8KPfpmp2hDJJ+rVJ","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124njeEtsLquJQ+2srPU3lz1FlRQ+gO17GYXFqoIjUFKq5"],"subtree_root_proofs":[{"start":14,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO8HNbaVEeRM+O1hBay7wGwdK8gfgw1UcN/2rlcvXjPTG","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+BYgIVspgB8oPRgn31f4UauroWLZd+Ml719urDeXx8+9","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+MGRBAZRg44q3VzQFeFqRYI+761+jGDc597Rnk4dvb3V","/////////////////////////////////////////////////////////////////////////////z6e4gxrnePPmnNXFAh0C4Hxag4WE2jEwAaC0kcrgulI"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckNvlIOuKAUPBTuG/2d9MfN/AaiwY71idaZdrMmq0NjAN","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckAT6xT/mkIu7EdOcqHT6kPjN57xvL8thnqlgmdPK2q8p","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckOS7vgw+OBNt3IxgdhJchLhARF3dX8aiFCnteBpdvqG/","/////////////////////////////////////////////////////////////////////////////5eAoa+9+6XecR94lkNN2ZLRP3cinm4Y6YG3YOV+/0jF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124h/mJb8eSc8itnPyFz5gI4KFyniTBfsMsZjCcry5Ym6a","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckK8NvBLXCIp/8g18echPjYjOpZbJb33wzQjwVATQ8SC/"],"proofs":[{"total":64,"index":4,"leaf_hash":"dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","aunts":["iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":5,"leaf_hash":"iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","aunts":["dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":4,"end_row":5},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A3jt+lpIIxDUcAzO6TCIq44qQM9/Jq/OJEIhxYZFZoBa","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A+48nJK0sVFUwal3v8P8yz/J1XsWWUyvFU0p2HaqpGPy","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A7jdOfOyet8CY12/m6fDAXn23JOV2exr8xJU6Wj/PjR5","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A3GHcpXxPZzs7Z6TYx8pFyAnu5YXfkRq5mO+p4eL4qwr","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A0WFy3qzAoUkE8ItTxsnD8o1+GZGri5dt4eJRj33RPI0","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A9dfKTkFi6xc45/mAnB9RTSLrdYGYv85/o0I7Lw7YZGj","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A7juuukHeXcJebH+xNj4cag46DRX6pTGexCDm3Z6MZpW","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A66WIIE7axylvdBY4tG2VyVVErD6LUsWzrwUDCl/fd0r","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A8caSA0JcF8KLhcfDMjTuQqc8E82/sEeXpzcC6PXujfm","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A9ZH9ytbQ8AnAmEtQkjkBl3VUKazW+qkLCBo7bsz3mOh"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckEoqNBF3TfD7yyGvcv6VwbLc1FNlqf62T9PU3UgDmzat","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckNHmIx6e8506hFK8n4kuyktc2RHf0ClU3pxhCNoVAW2E","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckGFUUdOuZ4D61DGhQsqs30lEaL+0CEASgCwlEYWngpJ7","/////////////////////////////////////////////////////////////////////////////xlKKjJ2l4vqk343ZLkvzPjGftcLkJ13KxLa311Lsek2"],"is_max_namespace_ignored":true},{"end":1,"nodes":["//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","//////////////////////////////////////7//////////////////////////////////////lrD0qJ9dspxSO1Yl8NDioZfgOm8Yj63Y+BGDRHlKCRj","//////////////////////////////////////7//////////////////////////////////////pNkZFSQk3XAFlq5+qVKnFnJIHi/zkuKy8dZMp9iFzwd","//////////////////////////////////////7//////////////////////////////////////iNa9Eq830VsdytAm6Xke+fTeD22gHTjIOVg5DYjsG9c","/////////////////////////////////////////////////////////////////////////////zfiVR658E7Sa0mEiv8dOg2yTJCOw6OZ4/yDJicPO0OD"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769Aw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A2PucNvKKC1F7TsSy5yV21AlOYU75A9apn0AttNPjuKv","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQP//////////////////////////////////////uHtZ4mv1XOcZFd5e2L4YID0bZRD7mxEVD2/qtGAVuqb"],"proofs":[{"total":64,"index":6,"leaf_hash":"rd9/DX4mhkvhKFkVrnb8+MnrtxLX4x43mO28rIbN8rU=","aunts":["WCH14xeikYnCmrCxD2ka5hQqqBWpYgNgM70ObGfkt6U=","7GlDZg9xEJsbzFtEseEyn0AUscDoKs7hs+sMhnYZtEE=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":7,"leaf_hash":"WCH14xeikYnCmrCxD2ka5hQqqBWpYgNgM70ObGfkt6U=","aunts":["rd9/DX4mhkvhKFkVrnb8+MnrtxLX4x43mO28rIbN8rU=","7GlDZg9xEJsbzFtEseEyn0AUscDoKs7hs+sMhnYZtEE=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":6,"end_row":7},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A3jt+lpIIxDUcAzO6TCIq44qQM9/Jq/OJEIhxYZFZoBa","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A+48nJK0sVFUwal3v8P8yz/J1XsWWUyvFU0p2HaqpGPy","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A7jdOfOyet8CY12/m6fDAXn23JOV2exr8xJU6Wj/PjR5","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A3GHcpXxPZzs7Z6TYx8pFyAnu5YXfkRq5mO+p4eL4qwr","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A0WFy3qzAoUkE8ItTxsnD8o1+GZGri5dt4eJRj33RPI0","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A9dfKTkFi6xc45/mAnB9RTSLrdYGYv85/o0I7Lw7YZGj","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A7juuukHeXcJebH+xNj4cag46DRX6pTGexCDm3Z6MZpW","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A66WIIE7axylvdBY4tG2VyVVErD6LUsWzrwUDCl/fd0r","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A8caSA0JcF8KLhcfDMjTuQqc8E82/sEeXpzcC6PXujfm","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A9ZH9ytbQ8AnAmEtQkjkBl3VUKazW+qkLCBo7bsz3mOh"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckEoqNBF3TfD7yyGvcv6VwbLc1FNlqf62T9PU3UgDmzat","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckNHmIx6e8506hFK8n4kuyktc2RHf0ClU3pxhCNoVAW2E","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckGFUUdOuZ4D61DGhQsqs30lEaL+0CEASgCwlEYWngpJ7","/////////////////////////////////////////////////////////////////////////////xlKKjJ2l4vqk343ZLkvzPjGftcLkJ13KxLa311Lsek2"],"is_max_namespace_ignored":true},{"end":1,"nodes":["//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","//////////////////////////////////////7//////////////////////////////////////lrD0qJ9dspxSO1Yl8NDioZfgOm8Yj63Y+BGDRHlKCRj","//////////////////////////////////////7//////////////////////////////////////pNkZFSQk3XAFlq5+qVKnFnJIHi/zkuKy8dZMp9iFzwd","//////////////////////////////////////7//////////////////////////////////////iNa9Eq830VsdytAm6Xke+fTeD22gHTjIOVg5DYjsG9c","/////////////////////////////////////////////////////////////////////////////zfiVR658E7Sa0mEiv8dOg2yTJCOw6OZ4/yDJicPO0OD"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769Aw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A2PucNvKKC1F7TsSy5yV21AlOYU75A9apn0AttNPjuKv","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQP//////////////////////////////////////uHtZ4mv1XOcZFd5e2L4YID0bZRD7mxEVD2/qtGAVuqb"],"proofs":[{"total":64,"index":6,"leaf_hash":"rd9/DX4mhkvhKFkVrnb8+MnrtxLX4x43mO28rIbN8rU=","aunts":["WCH14xeikYnCmrCxD2ka5hQqqBWpYgNgM70ObGfkt6U=","7GlDZg9xEJsbzFtEseEyn0AUscDoKs7hs+sMhnYZtEE=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":7,"leaf_hash":"WCH14xeikYnCmrCxD2ka5hQqqBWpYgNgM70ObGfkt6U=","aunts":["rd9/DX4mhkvhKFkVrnb8+MnrtxLX4x43mO28rIbN8rU=","7GlDZg9xEJsbzFtEseEyn0AUscDoKs7hs+sMhnYZtEE=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":6,"end_row":7},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0hEnxmwXh7886Vn1hLhdxRxA2P5c3mrw4F341xrzJ+oR","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0laRwIc7BADFhGdp2bE1OF3Nm4hSGIcC9cWj5zbNXrru","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0mbrUBLkq7Q8fuN0dc3xPfhZDIVf30FN7AbHAw/izPp/","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0spgVKbtJgNnGp+7RmT3i9gDRtawwz8c8wgDffwaBIhA","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0o0v4nhWIDYLqaHeM6QWfz5RHqQ4yTkKuTamLKk31bfX","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0ixfimxzICeKNMSmy704KH+xJsBa0j0ptkbeHq/R2+Hc","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0kPl5wLw4nOcxCUwC4doOcIJjzJ7rVN10OEKDmHwfxDG","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0kBBU04rW3do45ldRrHZMu2Vo9/kM2BWEBod5Pjg/lds","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0qxKstz76YPeOHEc8QwJxxKkbKuLzR6ccucejjOgA8XV","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0v3/Qmj+PWN3tr1oPJ4GwfPgMdZBeELOAXUFAnVsVoWE","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0gK2LVJzmJJR9WskspYJcRmXFa4Z2sTuPT/Vp5gHK+ii","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0makijsSyY99fZMcOn/7lpruayzGJm7ONXfUw+8nzDDd"],"subtree_root_proofs":[{"start":8,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBAzWDvj3qfq4tQgMmj+xrN70ohPnALkzsVfeSz2fDNR3I","//////////////////////////////////////////////////////////////////////////////RZ4DIjyeS33lzPqKfHa/fwfqhFKjA2JqDPkq53gZFS"],"is_max_namespace_ignored":true},{"end":4,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUylWThfS10n2lT6lE4LNW1HIyAFm2gAviE9kg4uLVO9A1","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUykr/RQCaggUWRpoUd7UC5bqRUkN+pZyDTOgBCqgu+4mU","/////////////////////////////////////////////////////////////////////////////ztJEbF7NawT8FbhPGOZU654FgVnxch9gF900JBMFcSI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0loLmE64NxCl3hnvl0FiRrM318oa6gvJ2ZXIQHKxT9b+","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyoIuCk7uuLaeGpMbJBTmPKJTs1FUXHhvwVRNmnsQW4eM"],"proofs":[{"total":64,"index":1,"leaf_hash":"JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","aunts":["LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":2,"leaf_hash":"ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","aunts":["ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":1,"end_row":2},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0hEnxmwXh7886Vn1hLhdxRxA2P5c3mrw4F341xrzJ+oR","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0laRwIc7BADFhGdp2bE1OF3Nm4hSGIcC9cWj5zbNXrru","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0mbrUBLkq7Q8fuN0dc3xPfhZDIVf30FN7AbHAw/izPp/","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0spgVKbtJgNnGp+7RmT3i9gDRtawwz8c8wgDffwaBIhA","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0o0v4nhWIDYLqaHeM6QWfz5RHqQ4yTkKuTamLKk31bfX","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0ixfimxzICeKNMSmy704KH+xJsBa0j0ptkbeHq/R2+Hc","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0kPl5wLw4nOcxCUwC4doOcIJjzJ7rVN10OEKDmHwfxDG","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0kBBU04rW3do45ldRrHZMu2Vo9/kM2BWEBod5Pjg/lds","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0qxKstz76YPeOHEc8QwJxxKkbKuLzR6ccucejjOgA8XV","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0v3/Qmj+PWN3tr1oPJ4GwfPgMdZBeELOAXUFAnVsVoWE","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0gK2LVJzmJJR9WskspYJcRmXFa4Z2sTuPT/Vp5gHK+ii","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0makijsSyY99fZMcOn/7lpruayzGJm7ONXfUw+8nzDDd"],"subtree_root_proofs":[{"start":8,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBAzWDvj3qfq4tQgMmj+xrN70ohPnALkzsVfeSz2fDNR3I","//////////////////////////////////////////////////////////////////////////////RZ4DIjyeS33lzPqKfHa/fwfqhFKjA2JqDPkq53gZFS"],"is_max_namespace_ignored":true},{"end":4,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUylWThfS10n2lT6lE4LNW1HIyAFm2gAviE9kg4uLVO9A1","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUykr/RQCaggUWRpoUd7UC5bqRUkN+pZyDTOgBCqgu+4mU","/////////////////////////////////////////////////////////////////////////////ztJEbF7NawT8FbhPGOZU654FgVnxch9gF900JBMFcSI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0loLmE64NxCl3hnvl0FiRrM318oa6gvJ2ZXIQHKxT9b+","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyoIuCk7uuLaeGpMbJBTmPKJTs1FUXHhvwVRNmnsQW4eM"],"proofs":[{"total":64,"index":1,"leaf_hash":"JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","aunts":["LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":2,"leaf_hash":"ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","aunts":["ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":1,"end_row":2},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyv9a1y1NsjyqYy2awZN5droMpDLJLY2Qwr2SeCR141Pc","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyp1XmAItl2yDylkbyJEc1ITbPYJ4qjYHVb5WWXrFFjno","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUytN064nXCnQCSizlnpBi4BrCh/7Y+617EEfyAZuBBvIc","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyocoJfaAGn1E4oXX7zUHRFkPMS8XG7I0X2h68NP25eFm","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyub8VAOdlLRUv5kp+Lyr34c3mZfEmgeFEq95cWYyYYde","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyj8b0OT1wNWhcR/THAk/hGVO3pU6ysxjeaaE/JWGWmc5","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyhcHJGt/s8A6jUWCKvZ7LR1JpMlAkyYo87p+/sZ8+PSb","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUylvhKkH0zBuQ+wvfAgw+iuNBRIlJm6itZsBib1RBoe7Q","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyiUacGg0m6s61ogdtMozEQdF9qeMu2sj2lAmI41JCh0c","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyuqio9LCcNO59auILR0mAyHCceyb1mNZPlURKxWQiz2N","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUygoM3yzrCxnG0zCEj3nQy58wnyKqRB78IhqdzLFLcahs","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUypLa7GLBm9kk+Vtrhh4KNc1Bza2lz63JUJt5+6FSoz1l","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUys37BGndM09PXgZG3hRJCBiVLbS5OBS81hZWlizw0nPU","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUytmq/27akZi/sDZxaMRYmENUlD/YSrdb5VlO2htmnrJ0"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0jlE/0U9NX0oxdKVYpG9hTOksEh3JfzgdNeO1Y7sp/d+","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFMMa/P95R4xN7nJANIonR0NOtyv2bgZpTHtO1ozqYNlO","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFBDteKQbcxJBcmqEXWlQIUEd5+2W1vDyZFjsDyhuIbYY","/////////////////////////////////////////////////////////////////////////////ztJEbF7NawT8FbhPGOZU654FgVnxch9gF900JBMFcSI"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO8N+TL/K2MCh9X38+4fp+ygXibUCFxnwRcXc7sLfh7In","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwOwk5BUojkMyAoxH+gsRa6fnZ4hfhMOttSnUWIWNLFbyQ","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwOxkqnbiSKuHDV3A4A2BQJ7e32ZRotOpLghUF0YP9cg6N","//////////////////////////////////////////////////////////////////////////////OMBZJBSP/bIc7DuXH9pfnNnH2Sd/m9vf6H9ePbzfRZ"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyoIuCk7uuLaeGpMbJBTmPKJTs1FUXHhvwVRNmnsQW4eM","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO+Wpa1810YUdXJ32jvBVM/CvrKtQv1Fvu0GsUlgbKuNL"],"proofs":[{"total":64,"index":2,"leaf_hash":"ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","aunts":["ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":3,"leaf_hash":"ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","aunts":["ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":2,"end_row":3},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyv9a1y1NsjyqYy2awZN5droMpDLJLY2Qwr2SeCR141Pc","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyp1XmAItl2yDylkbyJEc1ITbPYJ4qjYHVb5WWXrFFjno","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUytN064nXCnQCSizlnpBi4BrCh/7Y+617EEfyAZuBBvIc","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyocoJfaAGn1E4oXX7zUHRFkPMS8XG7I0X2h68NP25eFm","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyub8VAOdlLRUv5kp+Lyr34c3mZfEmgeFEq95cWYyYYde","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyj8b0OT1wNWhcR/THAk/hGVO3pU6ysxjeaaE/JWGWmc5","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyhcHJGt/s8A6jUWCKvZ7LR1JpMlAkyYo87p+/sZ8+PSb","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUylvhKkH0zBuQ+wvfAgw+iuNBRIlJm6itZsBib1RBoe7Q","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyiUacGg0m6s61ogdtMozEQdF9qeMu2sj2lAmI41JCh0c","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyuqio9LCcNO59auILR0mAyHCceyb1mNZPlURKxWQiz2N","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUygoM3yzrCxnG0zCEj3nQy58wnyKqRB78IhqdzLFLcahs","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUypLa7GLBm9kk+Vtrhh4KNc1Bza2lz63JUJt5+6FSoz1l","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUys37BGndM09PXgZG3hRJCBiVLbS5OBS81hZWlizw0nPU","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUytmq/27akZi/sDZxaMRYmENUlD/YSrdb5VlO2htmnrJ0"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0jlE/0U9NX0oxdKVYpG9hTOksEh3JfzgdNeO1Y7sp/d+","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFMMa/P95R4xN7nJANIonR0NOtyv2bgZpTHtO1ozqYNlO","AAAAAAAAAAAAAAAAAAAAAAAAAGS9k9rumyCorxQAAAAAAAAAAAAAAAAAAAAAAAAAZL2T2u6bIKivFBDteKQbcxJBcmqEXWlQIUEd5+2W1vDyZFjsDyhuIbYY","/////////////////////////////////////////////////////////////////////////////ztJEbF7NawT8FbhPGOZU654FgVnxch9gF900JBMFcSI"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO8N+TL/K2MCh9X38+4fp+ygXibUCFxnwRcXc7sLfh7In","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwOwk5BUojkMyAoxH+gsRa6fnZ4hfhMOttSnUWIWNLFbyQ","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwOxkqnbiSKuHDV3A4A2BQJ7e32ZRotOpLghUF0YP9cg6N","//////////////////////////////////////////////////////////////////////////////OMBZJBSP/bIc7DuXH9pfnNnH2Sd/m9vf6H9ePbzfRZ"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyoIuCk7uuLaeGpMbJBTmPKJTs1FUXHhvwVRNmnsQW4eM","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO+Wpa1810YUdXJ32jvBVM/CvrKtQv1Fvu0GsUlgbKuNL"],"proofs":[{"total":64,"index":2,"leaf_hash":"ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","aunts":["ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":3,"leaf_hash":"ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","aunts":["ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":2,"end_row":3},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww5vcq4o+eOvMV9rTnPFu8O7f1EsRaG3v0h26WCEZhlKm","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwwvyRj1VRqiCORlR43I4y0tlDySZbAmIl1n8Cpt7Rz41","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww3Jb0X3Zg2b3STkKjg3UtAgqiBVEn/hGL0CHKlvr6mQZ","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww1J84meLhHObXwtgXX1GkP20Ua8BsfZqZMqpXXDIdRN6","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww89hyT+CUp7rs1s603Q0wVmIvpwFk3Z0qhs1B9z/VCOX","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwxclG0Ybath4x6tjpyhj/UmGlPTmebOsTpHzfjvj4Pxg","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww9PeAGb9LmyyznFf2GAjhL1RP+1hMcrPQHhh0qvH3gb0","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww8lLz6S8myx5z5U/lSSZWpgGtiIXJFXwK0lnrQ7p0DK3","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww6ag6MycLdN6gtu5AEv7ayqkRT/tgFk2gjE28LSiA0lX","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww+42Y4wp0lZsqf+/LuT50jqw1phDVkyhP4orFVTXi1Jt","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww/LGZekzLa+OfRVCNn7CpwZzes/JWIGEB8IZdTuYbWzW","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww+pTD4yLYZFCZ3k5Up/AZs2JJbFxIWuUNuDRfpQLLn0/","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww6xPDm6i+ckzJfmUSoK3iKPFMnvYgVTVVen34MyJjM9+","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwz4IA+8bE70mbOgb5TQp56NL4DxrohuEp0GzFJRbkpKV","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww+IBmGFZNC9j4MJPgoGlWLNzlGsiB4/iOYR+7bX5lbZc","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww9Fes/YqjhPxbGB0cb7YLBD3LWGFh51AUQsaac7i0IiA"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABM+di3wCrKWUor+PrEpRlnbZIILIu+QJ8ZDpR0/iSoB6","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCbdAXx1mjBKprkXL/yPtbEujD49qf+seg0pRu0iVY1Q","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLrxpStnJAO8ZvyQ/qv9VKmSilfVIKy3hdx9ewMB7pF9","/////////////////////////////////////////////////////////////////////////////x/45QqgiM/Tmbw/YkoUIYwehkkhbhNJ8WsqIuCPB4Vu"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAB7FBOK48WeyQQMAAAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBA85yPkbe1uGziktblgdE1gymy7yX47yH4RZU8i77BlNJ","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0nS1Z999VJ8Vr7apR9kWg+6i2WfwSqzt2/ASX1Dwac3c","//////////////////////////////////////////////////////////////////////////////RZ4DIjyeS33lzPqKfHa/fwfqhFKjA2JqDPkq53gZFS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwww==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwzSce0ccYtclvk04FE5n2779nKnTquVkgpLRgXkXFmEz","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0loLmE64NxCl3hnvl0FiRrM318oa6gvJ2ZXIQHKxT9b+"],"proofs":[{"total":64,"leaf_hash":"LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","aunts":["JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":1,"leaf_hash":"JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","aunts":["LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"end_row":1},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww5vcq4o+eOvMV9rTnPFu8O7f1EsRaG3v0h26WCEZhlKm","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwwvyRj1VRqiCORlR43I4y0tlDySZbAmIl1n8Cpt7Rz41","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww3Jb0X3Zg2b3STkKjg3UtAgqiBVEn/hGL0CHKlvr6mQZ","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww1J84meLhHObXwtgXX1GkP20Ua8BsfZqZMqpXXDIdRN6","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww89hyT+CUp7rs1s603Q0wVmIvpwFk3Z0qhs1B9z/VCOX","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwxclG0Ybath4x6tjpyhj/UmGlPTmebOsTpHzfjvj4Pxg","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww9PeAGb9LmyyznFf2GAjhL1RP+1hMcrPQHhh0qvH3gb0","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww8lLz6S8myx5z5U/lSSZWpgGtiIXJFXwK0lnrQ7p0DK3","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww6ag6MycLdN6gtu5AEv7ayqkRT/tgFk2gjE28LSiA0lX","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww+42Y4wp0lZsqf+/LuT50jqw1phDVkyhP4orFVTXi1Jt","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww/LGZekzLa+OfRVCNn7CpwZzes/JWIGEB8IZdTuYbWzW","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww+pTD4yLYZFCZ3k5Up/AZs2JJbFxIWuUNuDRfpQLLn0/","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww6xPDm6i+ckzJfmUSoK3iKPFMnvYgVTVVen34MyJjM9+","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwz4IA+8bE70mbOgb5TQp56NL4DxrohuEp0GzFJRbkpKV","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww+IBmGFZNC9j4MJPgoGlWLNzlGsiB4/iOYR+7bX5lbZc","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAww9Fes/YqjhPxbGB0cb7YLBD3LWGFh51AUQsaac7i0IiA"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABM+di3wCrKWUor+PrEpRlnbZIILIu+QJ8ZDpR0/iSoB6","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCbdAXx1mjBKprkXL/yPtbEujD49qf+seg0pRu0iVY1Q","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLrxpStnJAO8ZvyQ/qv9VKmSilfVIKy3hdx9ewMB7pF9","/////////////////////////////////////////////////////////////////////////////x/45QqgiM/Tmbw/YkoUIYwehkkhbhNJ8WsqIuCPB4Vu"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAB7FBOK48WeyQQMAAAAAAAAAAAAAAAAAAAAAAAAAHsUE4rjxZ7JBA85yPkbe1uGziktblgdE1gymy7yX47yH4RZU8i77BlNJ","AAAAAAAAAAAAAAAAAAAAAAAAADmWGdebFaxWJdIAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0nS1Z999VJ8Vr7apR9kWg+6i2WfwSqzt2/ASX1Dwac3c","//////////////////////////////////////////////////////////////////////////////RZ4DIjyeS33lzPqKfHa/fwfqhFKjA2JqDPkq53gZFS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwww==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAFi6mt961WBAwwzSce0ccYtclvk04FE5n2779nKnTquVkgpLRgXkXFmEz","AAAAAAAAAAAAAAAAAAAAAAAAABYuprfetVgQMMMAAAAAAAAAAAAAAAAAAAAAAAAAOZYZ15sVrFYl0loLmE64NxCl3hnvl0FiRrM318oa6gvJ2ZXIQHKxT9b+"],"proofs":[{"total":64,"leaf_hash":"LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","aunts":["JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":1,"leaf_hash":"JiQZMpyDxZZxO2dfdJnfkUGK4dheNu9tbrMKLPpfHJo=","aunts":["LSrwP0tAOfoWTgaVE9UzU2MYw8h6y+1cSaUFLSWgdS0=","k76mo5ASyEriAxsKN+GclPhJWPHBCX8lWJqKPO5n90c=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"end_row":1},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124k2bBfOR0ATkN753OpwTW2giQxZwAkP6OHRjnAHVpLW0","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124njeEtsLquJQ+2srPU3lz1FlRQ+gO17GYXFqoIjUFKq5","/////////////////////////////////////////////////////////////////////////////5eAoa+9+6XecR94lkNN2ZLRP3cinm4Y6YG3YOV+/0jF"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A3jt+lpIIxDUcAzO6TCIq44qQM9/Jq/OJEIhxYZFZoBa","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769AxsJNyV3J5EiYZtaHq0Bm/By0FkeL4gIrplIlv2Rew2/","/////////////////////////////////////////////////////////////////////////////xlKKjJ2l4vqk343ZLkvzPjGftcLkJ13KxLa311Lsek2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckK8NvBLXCIp/8g18echPjYjOpZbJb33wzQjwVATQ8SC/","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A2PucNvKKC1F7TsSy5yV21AlOYU75A9apn0AttNPjuKv"],"proofs":[{"total":64,"index":5,"leaf_hash":"iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","aunts":["dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":6,"leaf_hash":"rd9/DX4mhkvhKFkVrnb8+MnrtxLX4x43mO28rIbN8rU=","aunts":["WCH14xeikYnCmrCxD2ka5hQqqBWpYgNgM70ObGfkt6U=","7GlDZg9xEJsbzFtEseEyn0AUscDoKs7hs+sMhnYZtEE=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":5,"end_row":6},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124k2bBfOR0ATkN753OpwTW2giQxZwAkP6OHRjnAHVpLW0","AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124njeEtsLquJQ+2srPU3lz1FlRQ+gO17GYXFqoIjUFKq5","/////////////////////////////////////////////////////////////////////////////5eAoa+9+6XecR94lkNN2ZLRP3cinm4Y6YG3YOV+/0jF"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A3jt+lpIIxDUcAzO6TCIq44qQM9/Jq/OJEIhxYZFZoBa","AAAAAAAAAAAAAAAAAAAAAAAAAOTUcZ+JU1++vQMAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769AxsJNyV3J5EiYZtaHq0Bm/By0FkeL4gIrplIlv2Rew2/","/////////////////////////////////////////////////////////////////////////////xlKKjJ2l4vqk343ZLkvzPjGftcLkJ13KxLa311Lsek2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAKl5gg6/54C9duIAAAAAAAAAAAAAAAAAAAAAAAAAtGIUSMUg+gWckK8NvBLXCIp/8g18echPjYjOpZbJb33wzQjwVATQ8SC/","AAAAAAAAAAAAAAAAAAAAAAAAALRiFEjFIPoFnJAAAAAAAAAAAAAAAAAAAAAAAAAA5NRxn4lTX769A2PucNvKKC1F7TsSy5yV21AlOYU75A9apn0AttNPjuKv"],"proofs":[{"total":64,"index":5,"leaf_hash":"iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","aunts":["dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":6,"leaf_hash":"rd9/DX4mhkvhKFkVrnb8+MnrtxLX4x43mO28rIbN8rU=","aunts":["WCH14xeikYnCmrCxD2ka5hQqqBWpYgNgM70ObGfkt6U=","7GlDZg9xEJsbzFtEseEyn0AUscDoKs7hs+sMhnYZtEE=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":5,"end_row":6},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyhylTh2ki5F6LDHEil5sQG1HhM47B6AP+ZdM0h7C8bA1","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUytmq/27akZi/sDZxaMRYmENUlD/YSrdb5VlO2htmnrJ0","//////////////////////////////////////////////////////////////////////////////OMBZJBSP/bIc7DuXH9pfnNnH2Sd/m9vf6H9ePbzfRZ"],"is_max_namespace_ignored":true},{"end":9,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+E0YhqM1mPlsAnBXYqKyrHOqigsP/U1HFCO3LzAKbzdN","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+HupJLjjgITQ+5Vlxq9C9cpRTSeCMkAYyfgfH57GQ6T+","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124mtfc4iUxBVXqbua3P9liD/3JA0ayqjMZTjlLDhNJK22","/////////////////////////////////////////////////////////////////////////////z6e4gxrnePPmnNXFAh0C4Hxag4WE2jEwAaC0kcrgulI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwOw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO+Wpa1810YUdXJ32jvBVM/CvrKtQv1Fvu0GsUlgbKuNL","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124h/mJb8eSc8itnPyFz5gI4KFyniTBfsMsZjCcry5Ym6a"],"proofs":[{"total":64,"index":3,"leaf_hash":"ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","aunts":["ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":4,"leaf_hash":"dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","aunts":["iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":3,"end_row":4},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":5,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUyhylTh2ki5F6LDHEil5sQG1HhM47B6AP+ZdM0h7C8bA1","AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAal8BCFVB0CNUytmq/27akZi/sDZxaMRYmENUlD/YSrdb5VlO2htmnrJ0","//////////////////////////////////////////////////////////////////////////////OMBZJBSP/bIc7DuXH9pfnNnH2Sd/m9vf6H9ePbzfRZ"],"is_max_namespace_ignored":true},{"end":9,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+E0YhqM1mPlsAnBXYqKyrHOqigsP/U1HFCO3LzAKbzdN","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAh7wipUFWfwPd+HupJLjjgITQ+5Vlxq9C9cpRTSeCMkAYyfgfH57GQ6T+","AAAAAAAAAAAAAAAAAAAAAAAAAIe8IqVBVn8D3fgAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124mtfc4iUxBVXqbua3P9liD/3JA0ayqjMZTjlLDhNJK22","/////////////////////////////////////////////////////////////////////////////z6e4gxrnePPmnNXFAh0C4Hxag4WE2jEwAaC0kcrgulI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwOw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGpfAQhVQdAjVMoAAAAAAAAAAAAAAAAAAAAAAAAAbHuwkFJyffRwO+Wpa1810YUdXJ32jvBVM/CvrKtQv1Fvu0GsUlgbKuNL","AAAAAAAAAAAAAAAAAAAAAAAAAGx7sJBScn30cDsAAAAAAAAAAAAAAAAAAAAAAAAAqXmCDr/ngL124h/mJb8eSc8itnPyFz5gI4KFyniTBfsMsZjCcry5Ym6a"],"proofs":[{"total":64,"index":3,"leaf_hash":"ePv7wquFijzfhEoURR5K6cu2uJSzqxyBcjs1dRdlN4Q=","aunts":["ysPZ346iK6SZC1utfIlI7AFGO89cNzBelFQCpu1rQO8=","sElIWqemt3w00AfU1VfgVPFr/Y4l1RyvVZI8Tzk0DOM=","39tQUzs8YeJOnVq8kq/PfNoGk4V/6NPxR3kejli2lC4=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]},{"total":64,"index":4,"leaf_hash":"dTSKNfN9GPo4siijUSnL/AsilBlAr3CZIpk08CuYWeE=","aunts":["iHYytBwtUMpzoAG+mBOsxqLxqIZhdeDoLIAG06aJjLc=","x8D4PMwarOrz9piGcCZw3Rc9Ol7wFLnNsEWWyop7IY0=","dgShq8au042QGYgeJ0unCirHl2oynhzujsg3i8JHg9g=","tF+rIhGHD+JtCdNRgNk5TVBC0lJF9DLBEsIeBAaT5hM=","g8Hcs0iKrNn8lMWANG+mRXDSrANU6Jx9AcpixLPspEM=","xH2eC8Xa2VNs+P6A8rZu3bDQc5oSwmnIKXZMYLQPJp8="]}],"start_row":3,"end_row":4},"namespace_version":0},"root":"gAtSO5sws9vvE9inD6Jd5OVxULgekUg3CONREged3Os=","sub_threshold":64}] \ No newline at end of file diff --git a/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65207e313020736861726573.json b/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65207e313020736861726573.json new file mode 100755 index 0000000000..7d4be3b31c --- /dev/null +++ b/blob/testdata/fuzz-corpus/verify-626c6f627320746861742074616b65207e313020736861726573.json @@ -0,0 +1 @@ +[{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCWFDZKKux9kxVj1BFz9j0Alzs0vPjhh1p+andPGvnwbk","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCXm0+EeOAo3ybq4QZUuPGK4GWQSPZVm8BImF5g0Ou8K7","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCVcCpwXFouWBLxukHRpXwuT/zzPbyRZHUp4CVNL20E8b","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCSMUZCDEHi/yBH+fBInpNs/bmyfjQEjLIQugfdOeIPtB","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCZCVkWfotDvq7wKg/DQfKwBMNVsuqYG3+CR149tzfcF2","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCVBwYOhRukFdiWS4EKSqCd6Tz8PHVO0A3vFk5FdOHaXU","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCRSF3E33iIL/7BoBsAt1XPIsL7yAkWHMoZST9XZpGSSL","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCTW2EhvPmAQR7WeJ5xaqHh3mjL0u6KMOvUg/lXE16oYB","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCbQ8x5DNeJm5Orlnghdn/y3iEVRq9DrOBTlxintyl0bM","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCcL0e6py2oANiwMiLmMPYd0oXp9tvkGKQyHXGLM9RODm","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCR6/56KrA7Y0LYRVeH5YQUcVDiY2faUl9KX6AyapH3Wy"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAWU6+22XII3hh6p2dPpfKx49ZG1UC9LhOC/BbowW22","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMKvuiVjNPe8fOK6FF9Nk9jSOKPiHCvjl2FFVVWGvbRr","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAckQmXbJteprlqxSrMrIUKGKLIHHX4fBVLrKEmxctnn","/////////////////////////////////////////////////////////////////////////////2fJeFbihf+nmAaje2RNnA1EVU9/rLZfp4DZYcnc03Ol"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXuBXGO+c01iSpegyVsP8yyP+Idv5ifatSS1Eojyj5Gwz","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXlEOreFHtPJP/uSDi90l9cH9TGGVJAiHPEc0xGmPuKCR","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXgtVbWzmiCESJHOc3XeRq9yq37nOBKcD4dKJndQO2bQC","/////////////////////////////////////////////////////////////////////////////03z4CXD1wMfL324aO/QCWtMvyfI6k6p5TrWLCC/pfnI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCZ7m7TS/3FrNmva6k+GtbeunS/8UHO5q/MXqx0FYK0ir","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXj+EmYiXztbrSuSd5XYCo+eqBeFMiF6Lfg6x7R8/QvTk"],"proofs":[{"total":64,"leaf_hash":"rlKMKp//RaS5fGQryRO7SRgXm4IVAE2DLKi8uylOLTE=","aunts":["zEI4pINhZtNxwU/W877sEKmYNu1sIBRjOcm6bsFFCOE=","mfvImAqr5ABScsfhwNMrICZ/jOTxpOktM6ZT4OwluWM=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":1,"leaf_hash":"zEI4pINhZtNxwU/W877sEKmYNu1sIBRjOcm6bsFFCOE=","aunts":["rlKMKp//RaS5fGQryRO7SRgXm4IVAE2DLKi8uylOLTE=","mfvImAqr5ABScsfhwNMrICZ/jOTxpOktM6ZT4OwluWM=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"end_row":1},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCWFDZKKux9kxVj1BFz9j0Alzs0vPjhh1p+andPGvnwbk","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCXm0+EeOAo3ybq4QZUuPGK4GWQSPZVm8BImF5g0Ou8K7","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCVcCpwXFouWBLxukHRpXwuT/zzPbyRZHUp4CVNL20E8b","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCSMUZCDEHi/yBH+fBInpNs/bmyfjQEjLIQugfdOeIPtB","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCZCVkWfotDvq7wKg/DQfKwBMNVsuqYG3+CR149tzfcF2","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCVBwYOhRukFdiWS4EKSqCd6Tz8PHVO0A3vFk5FdOHaXU","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCRSF3E33iIL/7BoBsAt1XPIsL7yAkWHMoZST9XZpGSSL","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCTW2EhvPmAQR7WeJ5xaqHh3mjL0u6KMOvUg/lXE16oYB","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCbQ8x5DNeJm5Orlnghdn/y3iEVRq9DrOBTlxintyl0bM","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCcL0e6py2oANiwMiLmMPYd0oXp9tvkGKQyHXGLM9RODm","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCR6/56KrA7Y0LYRVeH5YQUcVDiY2faUl9KX6AyapH3Wy"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAWU6+22XII3hh6p2dPpfKx49ZG1UC9LhOC/BbowW22","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMKvuiVjNPe8fOK6FF9Nk9jSOKPiHCvjl2FFVVWGvbRr","AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAckQmXbJteprlqxSrMrIUKGKLIHHX4fBVLrKEmxctnn","/////////////////////////////////////////////////////////////////////////////2fJeFbihf+nmAaje2RNnA1EVU9/rLZfp4DZYcnc03Ol"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXuBXGO+c01iSpegyVsP8yyP+Idv5ifatSS1Eojyj5Gwz","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXlEOreFHtPJP/uSDi90l9cH9TGGVJAiHPEc0xGmPuKCR","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXgtVbWzmiCESJHOc3XeRq9yq37nOBKcD4dKJndQO2bQC","/////////////////////////////////////////////////////////////////////////////03z4CXD1wMfL324aO/QCWtMvyfI6k6p5TrWLCC/pfnI"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCZ7m7TS/3FrNmva6k+GtbeunS/8UHO5q/MXqx0FYK0ir","AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXj+EmYiXztbrSuSd5XYCo+eqBeFMiF6Lfg6x7R8/QvTk"],"proofs":[{"total":64,"leaf_hash":"rlKMKp//RaS5fGQryRO7SRgXm4IVAE2DLKi8uylOLTE=","aunts":["zEI4pINhZtNxwU/W877sEKmYNu1sIBRjOcm6bsFFCOE=","mfvImAqr5ABScsfhwNMrICZ/jOTxpOktM6ZT4OwluWM=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":1,"leaf_hash":"zEI4pINhZtNxwU/W877sEKmYNu1sIBRjOcm6bsFFCOE=","aunts":["rlKMKp//RaS5fGQryRO7SRgXm4IVAE2DLKi8uylOLTE=","mfvImAqr5ABScsfhwNMrICZ/jOTxpOktM6ZT4OwluWM=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"end_row":1},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEpjFO4j12Mc+6CAvuUkV8K45dQ9C/FfL/QrXBM2SOm5J","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEuFEKQXu3SH49cDX/BJmFhvuZszxugAp8eOLF95BxKgR","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEsDOidFC9Ysen9WLTPDW/C8zbjN6zC9VquF78rnoS9RA","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEgFR0Vf3ZPEmNi8LwlNz0yZgOW37a5J7gCKO9wonEdBG","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegErX+vWrEN6VEO9qxpZqM31DGW6r29E+1Xd9DDCIqeSzI","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEiqCrccEsriw1BCqSnVoCn5g+vMd9sS9lqj8Q3xmNUdT","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEiu4LBPJanQRAXLk7aChlG8/sk0a4B0ZhKBre8whfJYe","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEvkDiniW6I9EyBO/4AuI5ERh22s7EunitlKFqZqqZ60v","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEtEdOVpV/ZTjnVwH4Kik30W1LcnmmAVCC7kxkKNb+pNW","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEiUMPDPPTI59ef0FvhX3YCCcH1iBnkfCfC309s25bJgR","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEkivK791sojDi8yJuwn6CKhcbpAYuIX/A5ccxFSXfXV7","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEtJq6SNLzYghHRVDikg02xcCZS3Pzbtoz7jLrdEifaPK","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEnQRCmnOnLlf/ZoFHDbkyVaGod8Tz3dMn+WmKiYEIocM"],"subtree_root_proofs":[{"start":11,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXjd/G6VcwfKUHIAPBd+0S1C0reDajJS3xFFwhaZA9lMf","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXsti6ZEFBpERj7RPQhvnRBBOZHI3xBqaTX5AS3QYJwm+","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXkOrQv7sRTo+Zt/T7/1L2j3ce2PHlRFTIOxHImqhx/TW","/////////////////////////////////////////////////////////////////////////////4vP+5R8uCrzevrZ5WVsrUe3HWtXnal/9CRArzhzbZ42"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoYAPsJ6Bh/PnsPVYMQbnqHTtuMX4hXq5J1LryM97ac8x","/////////////////////////////////////////////////////////////////////////////+M5uiSoSif01wSLeHwYHWoVla7os4Lk9pZbmncIlAcN"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEu87GGPAAylxHFwZ6sN4o2rD96ODAUf9FLRmX5yUwriM","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmofLh4ct9iF/4NKqRkTnA4YOgNo6jvQvxsx6TABKbNSAw"],"proofs":[{"total":64,"index":2,"leaf_hash":"p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","aunts":["Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":3,"leaf_hash":"Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","aunts":["p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":2,"end_row":3},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEpjFO4j12Mc+6CAvuUkV8K45dQ9C/FfL/QrXBM2SOm5J","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEuFEKQXu3SH49cDX/BJmFhvuZszxugAp8eOLF95BxKgR","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEsDOidFC9Ysen9WLTPDW/C8zbjN6zC9VquF78rnoS9RA","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEgFR0Vf3ZPEmNi8LwlNz0yZgOW37a5J7gCKO9wonEdBG","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegErX+vWrEN6VEO9qxpZqM31DGW6r29E+1Xd9DDCIqeSzI","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEiqCrccEsriw1BCqSnVoCn5g+vMd9sS9lqj8Q3xmNUdT","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEiu4LBPJanQRAXLk7aChlG8/sk0a4B0ZhKBre8whfJYe","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEvkDiniW6I9EyBO/4AuI5ERh22s7EunitlKFqZqqZ60v","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEtEdOVpV/ZTjnVwH4Kik30W1LcnmmAVCC7kxkKNb+pNW","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEiUMPDPPTI59ef0FvhX3YCCcH1iBnkfCfC309s25bJgR","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEkivK791sojDi8yJuwn6CKhcbpAYuIX/A5ccxFSXfXV7","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEtJq6SNLzYghHRVDikg02xcCZS3Pzbtoz7jLrdEifaPK","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEnQRCmnOnLlf/ZoFHDbkyVaGod8Tz3dMn+WmKiYEIocM"],"subtree_root_proofs":[{"start":11,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXjd/G6VcwfKUHIAPBd+0S1C0reDajJS3xFFwhaZA9lMf","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXsti6ZEFBpERj7RPQhvnRBBOZHI3xBqaTX5AS3QYJwm+","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXkOrQv7sRTo+Zt/T7/1L2j3ce2PHlRFTIOxHImqhx/TW","/////////////////////////////////////////////////////////////////////////////4vP+5R8uCrzevrZ5WVsrUe3HWtXnal/9CRArzhzbZ42"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoYAPsJ6Bh/PnsPVYMQbnqHTtuMX4hXq5J1LryM97ac8x","/////////////////////////////////////////////////////////////////////////////+M5uiSoSif01wSLeHwYHWoVla7os4Lk9pZbmncIlAcN"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEu87GGPAAylxHFwZ6sN4o2rD96ODAUf9FLRmX5yUwriM","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmofLh4ct9iF/4NKqRkTnA4YOgNo6jvQvxsx6TABKbNSAw"],"proofs":[{"total":64,"index":2,"leaf_hash":"p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","aunts":["Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":3,"leaf_hash":"Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","aunts":["p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":2,"end_row":3},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/oOsliMM9xPj3lD1jf/rLcimwFEoDCCn27aZLK8/RvQ2","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/sEsfXU3gDfbQqWJqQSndhGX2883msEnEna938CeqACQ","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/sSxQ91MKPfbjBNLEA2tWlFHSKO67pB4r9vRLL82opQH","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/npEhuxYYj17BmSGCUl+EC3Kryjuv6Hj2rIGQMwA/4fb","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/i2fzd5EVuyOmQP9B+Acg4hldFpnA0gTEtJvAP9Ivvtt","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/pmeUEuHslgBbgiZiph43LvPqYeJB+rPJZ/KUaBN7PZo","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hitZ37i7iZz8o0EkOw3erPl/Y4P8Ja3BWoXpxHhY98p","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/qj4iC9SwLv1qF/1w7PzYsmj81EYqj7FXOAeQXVWLpPn","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hBSmPYW8FlwQHPYs1ZMNQK24Xk1yNCLq9D2ijPjw+qr","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hJAL8eADodH3IDsUUqrnPIfCR3PWjBTPMaHeghOMq2b","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/t3HKqElpd1CwQlOh6EsdhTIdkNZd5/mCUT9WK+HqT4x","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/kyNVnVi51OHvl5TtJ4fIYAl+cg5f6FYkFDSozVm1qAM","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/nxLnp+bBbAEwP3zxHLyX/MGEit+ylm8NzmFeWXcOW0b","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hp3D0IAk/7DP6Cu49cIPkACTLr7wCDinnE/MhBRLMi8","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/uwMP9kLz5vQ7OASSAU7cTfs20WvSwpP5l8cX73MOoYJ"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8eZp34TAOLJhGIGPb/g2gi+cb0lOxgWT6CIuwcQWBogv","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8XKDAmC+zAhxvgu/aarSSrR4EBNQ1+HsawiU8T1/tHzN","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8bFcO6fXgzFbQ/Zo1ccnY903M1ZAAXQX6uRwFCBRXfQx","//////////////////////////////////////////////////////////////////////////////APl7RN5rkpCNcjHx5VxK7VbtlBo+GAu68k8iQV+dEe"],"is_max_namespace_ignored":true},{"end":6,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPqs5M+RwP0K20KHRToQQrGAvpHDoeSyquDBjoC/6yLWx","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPoQwjsgA/a0oQCKAldCLfIihu9DbrBWPMXKLo7YcbNBU","/////////////////////////////////////////////////////////////////////////////2DCPbfX5IP3OVz3qi+I8i8J9bdiBXv8wQ6M4YSsyDCB"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/kbQN2nofRhRCrLGGjwaCKAdbXJw0cAXWxAI3YHiVTgP","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPjfQsZrd9ld3r/WOAXfL7pnWq/FAxByh/NWnITDuv/ZA"],"proofs":[{"total":64,"index":7,"leaf_hash":"RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","aunts":["hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":8,"leaf_hash":"Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","aunts":["l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":7,"end_row":8},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/oOsliMM9xPj3lD1jf/rLcimwFEoDCCn27aZLK8/RvQ2","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/sEsfXU3gDfbQqWJqQSndhGX2883msEnEna938CeqACQ","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/sSxQ91MKPfbjBNLEA2tWlFHSKO67pB4r9vRLL82opQH","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/npEhuxYYj17BmSGCUl+EC3Kryjuv6Hj2rIGQMwA/4fb","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/i2fzd5EVuyOmQP9B+Acg4hldFpnA0gTEtJvAP9Ivvtt","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/pmeUEuHslgBbgiZiph43LvPqYeJB+rPJZ/KUaBN7PZo","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hitZ37i7iZz8o0EkOw3erPl/Y4P8Ja3BWoXpxHhY98p","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/qj4iC9SwLv1qF/1w7PzYsmj81EYqj7FXOAeQXVWLpPn","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hBSmPYW8FlwQHPYs1ZMNQK24Xk1yNCLq9D2ijPjw+qr","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hJAL8eADodH3IDsUUqrnPIfCR3PWjBTPMaHeghOMq2b","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/t3HKqElpd1CwQlOh6EsdhTIdkNZd5/mCUT9WK+HqT4x","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/kyNVnVi51OHvl5TtJ4fIYAl+cg5f6FYkFDSozVm1qAM","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/nxLnp+bBbAEwP3zxHLyX/MGEit+ylm8NzmFeWXcOW0b","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/hp3D0IAk/7DP6Cu49cIPkACTLr7wCDinnE/MhBRLMi8","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/uwMP9kLz5vQ7OASSAU7cTfs20WvSwpP5l8cX73MOoYJ"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8eZp34TAOLJhGIGPb/g2gi+cb0lOxgWT6CIuwcQWBogv","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8XKDAmC+zAhxvgu/aarSSrR4EBNQ1+HsawiU8T1/tHzN","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8bFcO6fXgzFbQ/Zo1ccnY903M1ZAAXQX6uRwFCBRXfQx","//////////////////////////////////////////////////////////////////////////////APl7RN5rkpCNcjHx5VxK7VbtlBo+GAu68k8iQV+dEe"],"is_max_namespace_ignored":true},{"end":6,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPqs5M+RwP0K20KHRToQQrGAvpHDoeSyquDBjoC/6yLWx","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPoQwjsgA/a0oQCKAldCLfIihu9DbrBWPMXKLo7YcbNBU","/////////////////////////////////////////////////////////////////////////////2DCPbfX5IP3OVz3qi+I8i8J9bdiBXv8wQ6M4YSsyDCB"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/kbQN2nofRhRCrLGGjwaCKAdbXJw0cAXWxAI3YHiVTgP","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPjfQsZrd9ld3r/WOAXfL7pnWq/FAxByh/NWnITDuv/ZA"],"proofs":[{"total":64,"index":7,"leaf_hash":"RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","aunts":["hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":8,"leaf_hash":"Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","aunts":["l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":7,"end_row":8},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":6,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/stnZv28+2IuKqUUIKjbJ9kJ0sg9XPqj6Q411SPhV9Xc","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/jA2bZXYifjBiNYK6NvvJQ0GF1uYq8ETXTUD/dHXvELF","/////////////////////////////////////////////////////////////////////////////2DCPbfX5IP3OVz3qi+I8i8J9bdiBXv8wQ6M4YSsyDCB"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiULjLBhgBo+npKZOFq3kE0HxTv4gUTBpjaK/qvyPqYPbv","AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUKIN5ZqI/3xNMYsz81tBJ4g7M3H0skHze0rR389VAkG6","/////////////////////////////////////////////////////////////////////////////xmNzpyzHLMADQJNhwCngTQ0iSkpDUjKtS+tYmYZOsF2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPjfQsZrd9ld3r/WOAXfL7pnWq/FAxByh/NWnITDuv/ZA","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUKRg7y3yoHvVKLGGljKF8N/FZOon2AcgleC6eMAKvv+G"],"proofs":[{"total":64,"index":8,"leaf_hash":"Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","aunts":["l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":9,"leaf_hash":"l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","aunts":["Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":8,"end_row":9},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":6,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/stnZv28+2IuKqUUIKjbJ9kJ0sg9XPqj6Q411SPhV9Xc","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/jA2bZXYifjBiNYK6NvvJQ0GF1uYq8ETXTUD/dHXvELF","/////////////////////////////////////////////////////////////////////////////2DCPbfX5IP3OVz3qi+I8i8J9bdiBXv8wQ6M4YSsyDCB"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiULjLBhgBo+npKZOFq3kE0HxTv4gUTBpjaK/qvyPqYPbv","AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUKIN5ZqI/3xNMYsz81tBJ4g7M3H0skHze0rR389VAkG6","/////////////////////////////////////////////////////////////////////////////xmNzpyzHLMADQJNhwCngTQ0iSkpDUjKtS+tYmYZOsF2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPjfQsZrd9ld3r/WOAXfL7pnWq/FAxByh/NWnITDuv/ZA","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUKRg7y3yoHvVKLGGljKF8N/FZOon2AcgleC6eMAKvv+G"],"proofs":[{"total":64,"index":8,"leaf_hash":"Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","aunts":["l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":9,"leaf_hash":"l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","aunts":["Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":8,"end_row":9},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEsfzsZR+nZQuU3ZO9VNGvXitX9IHn/LBVS6fucYvDFfZ","/////////////////////////////////////////////////////////////////////////////+M5uiSoSif01wSLeHwYHWoVla7os4Lk9pZbmncIlAcN"],"is_max_namespace_ignored":true},{"end":11,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJoW5iOeGBTPlpTITiIgf1JOdvQkUcxnt6tfrVs16YTP","AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJWvHrVFU8fMijVb1HryNf7rnR5dasc7Q69fxC7DjNX1","/////////////////////////////////////////////////////////////////////////////ztKJeWOeB053ipD73q/UZLyyfXP2YTsRF2ytoIZGFCG"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmofLh4ct9iF/4NKqRkTnA4YOgNo6jvQvxsx6TABKbNSAw","AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJ1f7GoQPkJlCTlHkbiSq4jEQWZBy3oOsgiLso7KNws0"],"proofs":[{"total":64,"index":3,"leaf_hash":"Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","aunts":["p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":4,"leaf_hash":"1NOklXBsvJ9L2wf0gTW4V2ISVhySBWXl/l7f6C7u4uo=","aunts":["R12OQ+yzYhM8FoHZVwFxH6nfizR9nNncLJEG2Q3P/oU=","+nZm8dlyvoiKha/YPGcxhUo3xGxbCEbOOJLkfHF8Cyc=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":3,"end_row":4},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEsfzsZR+nZQuU3ZO9VNGvXitX9IHn/LBVS6fucYvDFfZ","/////////////////////////////////////////////////////////////////////////////+M5uiSoSif01wSLeHwYHWoVla7os4Lk9pZbmncIlAcN"],"is_max_namespace_ignored":true},{"end":11,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJoW5iOeGBTPlpTITiIgf1JOdvQkUcxnt6tfrVs16YTP","AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJWvHrVFU8fMijVb1HryNf7rnR5dasc7Q69fxC7DjNX1","/////////////////////////////////////////////////////////////////////////////ztKJeWOeB053ipD73q/UZLyyfXP2YTsRF2ytoIZGFCG"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmofLh4ct9iF/4NKqRkTnA4YOgNo6jvQvxsx6TABKbNSAw","AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJ1f7GoQPkJlCTlHkbiSq4jEQWZBy3oOsgiLso7KNws0"],"proofs":[{"total":64,"index":3,"leaf_hash":"Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","aunts":["p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":4,"leaf_hash":"1NOklXBsvJ9L2wf0gTW4V2ISVhySBWXl/l7f6C7u4uo=","aunts":["R12OQ+yzYhM8FoHZVwFxH6nfizR9nNncLJEG2Q3P/oU=","+nZm8dlyvoiKha/YPGcxhUo3xGxbCEbOOJLkfHF8Cyc=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":3,"end_row":4},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlGzjzFrKIP/GppuzoUGUU8LWigVwvaNae5bp2MzHJCz8","/////////////////////////////////////////////////////////////////////////////wYlzquH1H1d5tIryoZKMyRxfyH2ct/c+g7RRatA2jgF"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/oOsliMM9xPj3lD1jf/rLcimwFEoDCCn27aZLK8/RvQ2","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/rrc+2XHqODdLbnpLHWGmAHfjs/4+wvANCs5aTXJ2C68","//////////////////////////////////////////////////////////////////////////////APl7RN5rkpCNcjHx5VxK7VbtlBo+GAu68k8iQV+dEe"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8Q==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8S84yH8EOAKh5m4SEJIVwx0j5s4NIexXGtAFmHhzD0uI","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/kbQN2nofRhRCrLGGjwaCKAdbXJw0cAXWxAI3YHiVTgP"],"proofs":[{"total":64,"index":6,"leaf_hash":"hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","aunts":["RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":7,"leaf_hash":"RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","aunts":["hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":6,"end_row":7},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlGzjzFrKIP/GppuzoUGUU8LWigVwvaNae5bp2MzHJCz8","/////////////////////////////////////////////////////////////////////////////wYlzquH1H1d5tIryoZKMyRxfyH2ct/c+g7RRatA2jgF"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/oOsliMM9xPj3lD1jf/rLcimwFEoDCCn27aZLK8/RvQ2","AAAAAAAAAAAAAAAAAAAAAAAAAIRrvZg35TGZKf4AAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/rrc+2XHqODdLbnpLHWGmAHfjs/4+wvANCs5aTXJ2C68","//////////////////////////////////////////////////////////////////////////////APl7RN5rkpCNcjHx5VxK7VbtlBo+GAu68k8iQV+dEe"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8Q==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8S84yH8EOAKh5m4SEJIVwx0j5s4NIexXGtAFmHhzD0uI","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAhGu9mDflMZkp/kbQN2nofRhRCrLGGjwaCKAdbXJw0cAXWxAI3YHiVTgP"],"proofs":[{"total":64,"index":6,"leaf_hash":"hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","aunts":["RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":7,"leaf_hash":"RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","aunts":["hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":6,"end_row":7},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":11,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoQ/llaZlyORfDbcPyDFzh5eRk94PdaOHk9K845gfBHKe","AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoRryYOPf3Hjnl58hjsmD5ko+Lo2BvLOHfIOjbV4hdLPq","AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoWyS00G5C6Fi/9lPKEqTo/dmStcrj8HWMnuwLeGJhBO6","/////////////////////////////////////////////////////////////////////////////ztKJeWOeB053ipD73q/UZLyyfXP2YTsRF2ytoIZGFCG"],"is_max_namespace_ignored":true},{"end":16,"nodes":["/////////////////////////////////////////////////////////////////////////////94oXQPwhcaAMBF6GySEGp/Ftfo6geusFnwpuHXP1IJK"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8V/bUTwsjFQXdnmx0ZQOQoL7SqgsrXbgIsWCj6nGoi8Z","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8XWIA2h1Q6kgCPa+DKiQsJjlDN5Vix+cESYUuqiUbff1","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8aLQyViZSIj6QW1E2E6PBl/8vuutLLPPdKIkVT14jxx+","/////////////////////////////////////////////////////////////////////////////wYlzquH1H1d5tIryoZKMyRxfyH2ct/c+g7RRatA2jgF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJ1f7GoQPkJlCTlHkbiSq4jEQWZBy3oOsgiLso7KNws0","AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlGaCvJ+zJ0YWJYdcBD7a1RiTI95Kf8Qrxg4VMBmnH+Pu","AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8S84yH8EOAKh5m4SEJIVwx0j5s4NIexXGtAFmHhzD0uI"],"proofs":[{"total":64,"index":4,"leaf_hash":"1NOklXBsvJ9L2wf0gTW4V2ISVhySBWXl/l7f6C7u4uo=","aunts":["R12OQ+yzYhM8FoHZVwFxH6nfizR9nNncLJEG2Q3P/oU=","+nZm8dlyvoiKha/YPGcxhUo3xGxbCEbOOJLkfHF8Cyc=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":5,"leaf_hash":"R12OQ+yzYhM8FoHZVwFxH6nfizR9nNncLJEG2Q3P/oU=","aunts":["1NOklXBsvJ9L2wf0gTW4V2ISVhySBWXl/l7f6C7u4uo=","+nZm8dlyvoiKha/YPGcxhUo3xGxbCEbOOJLkfHF8Cyc=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":6,"leaf_hash":"hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","aunts":["RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":4,"end_row":6},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":11,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoQ/llaZlyORfDbcPyDFzh5eRk94PdaOHk9K845gfBHKe","AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoRryYOPf3Hjnl58hjsmD5ko+Lo2BvLOHfIOjbV4hdLPq","AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAcfZ+M9kZeUbmoWyS00G5C6Fi/9lPKEqTo/dmStcrj8HWMnuwLeGJhBO6","/////////////////////////////////////////////////////////////////////////////ztKJeWOeB053ipD73q/UZLyyfXP2YTsRF2ytoIZGFCG"],"is_max_namespace_ignored":true},{"end":16,"nodes":["/////////////////////////////////////////////////////////////////////////////94oXQPwhcaAMBF6GySEGp/Ftfo6geusFnwpuHXP1IJK"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8V/bUTwsjFQXdnmx0ZQOQoL7SqgsrXbgIsWCj6nGoi8Z","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8XWIA2h1Q6kgCPa+DKiQsJjlDN5Vix+cESYUuqiUbff1","AAAAAAAAAAAAAAAAAAAAAAAAAIDxT/OESVubBPEAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8aLQyViZSIj6QW1E2E6PBl/8vuutLLPPdKIkVT14jxx+","/////////////////////////////////////////////////////////////////////////////wYlzquH1H1d5tIryoZKMyRxfyH2ct/c+g7RRatA2jgF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHH2fjPZGXlG5qEAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlJ1f7GoQPkJlCTlHkbiSq4jEQWZBy3oOsgiLso7KNws0","AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAe8f3kRlun+/HlGaCvJ+zJ0YWJYdcBD7a1RiTI95Kf8Qrxg4VMBmnH+Pu","AAAAAAAAAAAAAAAAAAAAAAAAAHvH95EZbp/vx5QAAAAAAAAAAAAAAAAAAAAAAAAAgPFP84RJW5sE8S84yH8EOAKh5m4SEJIVwx0j5s4NIexXGtAFmHhzD0uI"],"proofs":[{"total":64,"index":4,"leaf_hash":"1NOklXBsvJ9L2wf0gTW4V2ISVhySBWXl/l7f6C7u4uo=","aunts":["R12OQ+yzYhM8FoHZVwFxH6nfizR9nNncLJEG2Q3P/oU=","+nZm8dlyvoiKha/YPGcxhUo3xGxbCEbOOJLkfHF8Cyc=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":5,"leaf_hash":"R12OQ+yzYhM8FoHZVwFxH6nfizR9nNncLJEG2Q3P/oU=","aunts":["1NOklXBsvJ9L2wf0gTW4V2ISVhySBWXl/l7f6C7u4uo=","+nZm8dlyvoiKha/YPGcxhUo3xGxbCEbOOJLkfHF8Cyc=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":6,"leaf_hash":"hqsRnnV4oIpliLMYs+OBGU9Jz+5Hi8DXnMY3c71cm/o=","aunts":["RCz8qGrBFQrHIeivfIttMWGKZTbz13lo5Kys1Bms9r4=","GSY892VVmEG/M2PMyfqc6ixHx11W3NYxpK7B2m2gg5s=","kdAwDlqR4scRLNLKUfCRbQTrPXnhfWmoD4uE9IFbuW4=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":4,"end_row":6},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCc5MLsYND0zeP5jFDhFuoTCU2TqkQOsGoQblfGcXVrVK","/////////////////////////////////////////////////////////////////////////////03z4CXD1wMfL324aO/QCWtMvyfI6k6p5TrWLCC/pfnI"],"is_max_namespace_ignored":true},{"end":11,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEpjFO4j12Mc+6CAvuUkV8K45dQ9C/FfL/QrXBM2SOm5J","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEvTzXGNmnSOx9QgK40Lhl9RJPgp2FihvXqZf/KTQkE7w","/////////////////////////////////////////////////////////////////////////////4vP+5R8uCrzevrZ5WVsrUe3HWtXnal/9CRArzhzbZ42"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXj+EmYiXztbrSuSd5XYCo+eqBeFMiF6Lfg6x7R8/QvTk","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEu87GGPAAylxHFwZ6sN4o2rD96ODAUf9FLRmX5yUwriM"],"proofs":[{"total":64,"index":1,"leaf_hash":"zEI4pINhZtNxwU/W877sEKmYNu1sIBRjOcm6bsFFCOE=","aunts":["rlKMKp//RaS5fGQryRO7SRgXm4IVAE2DLKi8uylOLTE=","mfvImAqr5ABScsfhwNMrICZ/jOTxpOktM6ZT4OwluWM=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":2,"leaf_hash":"p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","aunts":["Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":1,"end_row":2},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXhTUpUeB2sbSJ9LNtJQ9c7Q1e4ocKwAsIWBcc989WVnm","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXqaPwe2RhSLnTRnGGU8ituHWlxgVccTxUk+EqVNbvtud","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXgsPdwwJNMt99i8UrNvEHLbBwyx5Qj/T3v6GZOykulFN","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXoMsWKl6fPU8lRAMB7CjbjL+/LII+ga1pEbrjf17DZHJ","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXtnLaHHkbfD/5F2yRw3dm/gNPl/C01AJZtsBut4F4A0P","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXkh17Ry/vkxjoJ7ckh/kCrFVIO2NBM9oW3rqLDzWyo71","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXimfc/MDW/Plv6r6VbWqvoliF1iROLbMjafru+uuq2h/","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXv0ChA10DoPuNLpBm8d/br+tn/AwBSUIND3jRcFWqXHf","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXk9SY36yd0iiWSpkhezyMIuZYGTHpWApBFietF18V0x0","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXrH4PoKuHcBT2nazOkwurHcF+Lh2RhBGM3BcaXBUjNkW","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXmnPlxGv4brTEHmyvSqDHvdKIfoLwia7iPxl7P8dZzSu","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXm3163g1MVUewpjlzGvZsu9EiOrcBSmGzaqfY2GxTYqE","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXm3kVz+n7Y+8XG6SfnYCqLh4b/l6Hwt/JVSjhAlMZw0v","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXsXujKiQOyFfbkA0W9hKVAu5SMfuVJhC4vVL9gX8YuxZ","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXjuESNg8fhjsUnxlG3f85sOVSQwnorgcE5z/CEuke6Um","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXh6uDYVjlM0SeEAQE4A2JMUEPvZflIZdksiNltIwJqdP","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXoucXJV/ouAcYZptcsBseU/4Z1Cui5UG7x561n+ogP86","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXrnUYueygO85v6TL5y1S2TlQ5H1U/5Bji2bsH9dzo8vQ","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXljMAwf7ssWRnR2GWUabI7TS9xDQHoUPnbeTxnFUobnn","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXjj4mkDpoqFVgvj7cJqoKsmB7ElNk8ae7yQOOVGpVUyS","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXt1s6qblgC3bAayc+Dqw7ZXGn2C3+wg9Ns9pLWcYNNaq","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXhjkW46MFUkbPPHhQokR02YHW6CVT4MI+/ivYMS76rYJ","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXk7jvkPc2NzQms4IZnVLYcFr9ukAeKdAl14SNVmBs4QI","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXp0BNgvtafM7Xn/W2m2zk30Xmv1HLnfMC06B0lyDYvNn","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXkOrQv7sRTo+Zt/T7/1L2j3ce2PHlRFTIOxHImqhx/TW"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAEf41Eahm+ETYCc5MLsYND0zeP5jFDhFuoTCU2TqkQOsGoQblfGcXVrVK","/////////////////////////////////////////////////////////////////////////////03z4CXD1wMfL324aO/QCWtMvyfI6k6p5TrWLCC/pfnI"],"is_max_namespace_ignored":true},{"end":11,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEpjFO4j12Mc+6CAvuUkV8K45dQ9C/FfL/QrXBM2SOm5J","AAAAAAAAAAAAAAAAAAAAAAAAAG3YbPVOt9HHoBIAAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEvTzXGNmnSOx9QgK40Lhl9RJPgp2FihvXqZf/KTQkE7w","/////////////////////////////////////////////////////////////////////////////4vP+5R8uCrzevrZ5WVsrUe3HWtXnal/9CRArzhzbZ42"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAABH+NRGoZvhE2AkAAAAAAAAAAAAAAAAAAAAAAAAAZtdfbMd90YYAXj+EmYiXztbrSuSd5XYCo+eqBeFMiF6Lfg6x7R8/QvTk","AAAAAAAAAAAAAAAAAAAAAAAAAGbXX2zHfdGGAF4AAAAAAAAAAAAAAAAAAAAAAAAAbdhs9U630cegEu87GGPAAylxHFwZ6sN4o2rD96ODAUf9FLRmX5yUwriM"],"proofs":[{"total":64,"index":1,"leaf_hash":"zEI4pINhZtNxwU/W877sEKmYNu1sIBRjOcm6bsFFCOE=","aunts":["rlKMKp//RaS5fGQryRO7SRgXm4IVAE2DLKi8uylOLTE=","mfvImAqr5ABScsfhwNMrICZ/jOTxpOktM6ZT4OwluWM=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":2,"leaf_hash":"p9l01wITMca8ux6C4spL+Sa9so9p+OdoAFvzcAX1bp0=","aunts":["Z5xXNR0JWwjAiChQQ3ujmkQBKe/KpnWZuEBwogEfqkc=","TOyk4Mllis52U5mjLy96z3EUsAECWrf5VBEr3q/L0Jo=","S1Qcy0umqfz+T/Q+9W9ePexy35kQ9IdJ5/Nvs2bjK1k=","7cf9Yp1XVZSCMK9+HidizTzvNe24SQqXZPrrvPLAIt4=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":1,"end_row":2},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPiBaG7PMdYT0u8a9pa7Z6LySOTtrObKB9h+xf0Gffo5G","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPp9KUEe8/bI71mt9HJfmAHp2eLtDFpb/UoDZfOwBGnJC","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPqy1lxUDmmEjv9Cq6ymWc+NKDmuy5RanNeH6aa0vs+wI","/////////////////////////////////////////////////////////////////////////////xmNzpyzHLMADQJNhwCngTQ0iSkpDUjKtS+tYmYZOsF2"],"is_max_namespace_ignored":true},{"end":16,"nodes":["//////////////////////////////////////////////////////////////////////////////cfFaRM0YhZPGEWOyEFt4krZk3jVnohVyVeRaPHYDIY"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfKerYkw1sE4+KnJJ46H4WIyZ0qUUbGQoXp04OA+PrVli","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfAleezptz8nf/ezY/TMYwXbPO8JoCqWP+dU3T8vsaP4U","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfO/CeNFr4FixhqfLDfj36cMr73JwEz4uZ+Qqa2ko3MK5","/////////////////////////////////////////////////////////////////////////////3QU2v3YBCNgsZp2axFECpWIT3Q/MYuYfaPCcOhBkIve"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUKRg7y3yoHvVKLGGljKF8N/FZOon2AcgleC6eMAKvv+G","AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUPQu+zpxS3SJaeR/7mPB67bzXsP8ioiRqJL3OHwsYs3C","AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfPpTsEZuT4NBJf71KAKkxYHSDUXqC29CWgJ8NXLSNvY6"],"proofs":[{"total":64,"index":9,"leaf_hash":"l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","aunts":["Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":10,"leaf_hash":"rx5b8PibXfRgtLtixVPkQXAIbBwuq9JoLfxtod58cks=","aunts":["y70vcVWrRBLSpVXzIpCyN1boiAhLqbsYH8e1MhqNyR8=","RsQnzT8vYsQKo7aMmKxmxOZCxfE5Wx4VGmsWCeOMfj4=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":11,"leaf_hash":"y70vcVWrRBLSpVXzIpCyN1boiAhLqbsYH8e1MhqNyR8=","aunts":["rx5b8PibXfRgtLtixVPkQXAIbBwuq9JoLfxtod58cks=","RsQnzT8vYsQKo7aMmKxmxOZCxfE5Wx4VGmsWCeOMfj4=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":9,"end_row":11},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":7,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPiBaG7PMdYT0u8a9pa7Z6LySOTtrObKB9h+xf0Gffo5G","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPp9KUEe8/bI71mt9HJfmAHp2eLtDFpb/UoDZfOwBGnJC","AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAnklRbZsvdmOGPqy1lxUDmmEjv9Cq6ymWc+NKDmuy5RanNeH6aa0vs+wI","/////////////////////////////////////////////////////////////////////////////xmNzpyzHLMADQJNhwCngTQ0iSkpDUjKtS+tYmYZOsF2"],"is_max_namespace_ignored":true},{"end":16,"nodes":["//////////////////////////////////////////////////////////////////////////////cfFaRM0YhZPGEWOyEFt4krZk3jVnohVyVeRaPHYDIY"],"is_max_namespace_ignored":true},{"end":2,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfKerYkw1sE4+KnJJ46H4WIyZ0qUUbGQoXp04OA+PrVli","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfAleezptz8nf/ezY/TMYwXbPO8JoCqWP+dU3T8vsaP4U","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfO/CeNFr4FixhqfLDfj36cMr73JwEz4uZ+Qqa2ko3MK5","/////////////////////////////////////////////////////////////////////////////3QU2v3YBCNgsZp2axFECpWIT3Q/MYuYfaPCcOhBkIve"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAJ5JUW2bL3Zjhj4AAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUKRg7y3yoHvVKLGGljKF8N/FZOon2AcgleC6eMAKvv+G","AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUPQu+zpxS3SJaeR/7mPB67bzXsP8ioiRqJL3OHwsYs3C","AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfPpTsEZuT4NBJf71KAKkxYHSDUXqC29CWgJ8NXLSNvY6"],"proofs":[{"total":64,"index":9,"leaf_hash":"l9NZLg4XkqIM2KCIQHhozbRQcmInWIviJ5XeclWkX+I=","aunts":["Sgsx82e4Sst+8/w+wDuKdXYQV/hjA+P1+3Lim0yG4CA=","jasdqYh9oLvkAVe7HlSVRG8dXKXzBeuZdAzWi53/efg=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":10,"leaf_hash":"rx5b8PibXfRgtLtixVPkQXAIbBwuq9JoLfxtod58cks=","aunts":["y70vcVWrRBLSpVXzIpCyN1boiAhLqbsYH8e1MhqNyR8=","RsQnzT8vYsQKo7aMmKxmxOZCxfE5Wx4VGmsWCeOMfj4=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":11,"leaf_hash":"y70vcVWrRBLSpVXzIpCyN1boiAhLqbsYH8e1MhqNyR8=","aunts":["rx5b8PibXfRgtLtixVPkQXAIbBwuq9JoLfxtod58cks=","RsQnzT8vYsQKo7aMmKxmxOZCxfE5Wx4VGmsWCeOMfj4=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":9,"end_row":11},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfH50iywzI2cN2jN1j+IJIWlUGphPTXgcKFXvnh6tTfH1","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfIhyWIsGMUG8IddkbswoOLS2PaF5yfMGRTrKHU3rdq2C","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfM95KDyIOJkb2a6tx+Pt6Ms3MFMgjjqIaTcHppPNxPh8","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfB1zYHvegdPbZ2qyKTb6JSjzley9TRBwYUGJSIPRjM+4","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfMEKU7Lq6n2uuYrNNkwYFeuwEE92Dlyg2iUPQmCWZhEZ","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfL9mHdGQx61PTBsC1nVs/XmeLFda98appo2yFLyOKmIQ","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfEgetlsx79bfYOsrc3qLmZu/IdR897dKU/Gr18cLV5A1","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfFYSSYH9hixlwblBACTfeurt1wsiEbqCemmPg3vws5S9","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfAfEaIDfVyH6t8Ylmt+6fH4UaJnlF8p2j9c9CxBLBu1m","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfAz1+Z7WFGrHBOZXSJx9Wj1KEbWM+kzvzigR/TMwl0xt","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfKf96FaPM9Zj0CjZlxxLKCIxLyyKOEf1WpveMGeXlFLi","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfCzMTX+Ce/N58kVeaUDUdDY983PJ9aWBGcI2Heim1Bre","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLK2p/LVXONsbLzZrRgUo+uW2j1bXi6vPlbt94jORrMW","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfOuQD8GDNiFJWIdELplCVcsD3lU+Amw/TrHSygOH6Lqr","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfFuD+Q6YJ/PKW8dZYGqeQVar5uMRX6TtnRhaGVq13GBy","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfO0LUImuNaNqdisMhDzvmyHdzpjVONcRHNRjt3mrn+ga","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfOp4rKaycq4Pvq6GQgr4Ww/ZXbtbkEi4I9/0AyKUkCyf","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfP8QW46QVu5hSw6Xy59NLjc7dT5rdTRtXXiDO2zSSsbG","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLsAKHNSuF2HyoNlKbbgA8/BXtdFKcpnIQVv6o3FzREq","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLa8q4XZPC47IWKwH1qmfFmwFezEhTiDtmuDxSIuGs25","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfNDMwVLAxjRVJJvkzCl6kkBR/PVBYQ42+IX2MvWm+cOx","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfH9IexMaks64crpbpgfxOr3nv5V7QlWdI+sTWeG5b8CC","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfECsZ11XxgKLU1440wpCycy1rDjwd06ZohmO7N8qp0rv","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfIUWHPMPmh9Xj60xZWhbhoQsc1ImPJp8SyGk6LOTo3UG","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfFqvYTMd8App1jVSmDxezph5U/rjx9SRiBx1/prV6x7x","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfBLB52v3dpqCyfZMwDjQOeoxrfr6bqqbpOVpdfv+hiMx","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfDJgRRBRC54XvWc2Nz2jp23+4WLesR+U4tEGO7ClvaGW","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfMBA/0YnBIAENOWoWxGZdscIy0w+Hfbk5NKLwiVIHTCE","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfNcqj7I1n3grVKvX5ydiXLzorbm5Qn2HGA1ymzecza00","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLY9XlzKP5kGy8rE/mH7ETJIy6AuEPLNOGbPpcErS5nL"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUMmWzTso2HbpAN6XeCB47azXrJ2snd4tX85wckwfgXQx","/////////////////////////////////////////////////////////////////////////////3QU2v3YBCNgsZp2axFECpWIT3Q/MYuYfaPCcOhBkIve"],"is_max_namespace_ignored":true},{"end":16,"nodes":["/////////////////////////////////////////////////////////////////////////////w721aysxkh3qg2sJilwwj0NIfz19tbdMJlwp2Q98B3j"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfPpTsEZuT4NBJf71KAKkxYHSDUXqC29CWgJ8NXLSNvY6","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfN3m156KlGPp0ioirP7SOo7ZOD3sBgK5yWTaOk4OnkfZ"],"proofs":[{"total":64,"index":11,"leaf_hash":"y70vcVWrRBLSpVXzIpCyN1boiAhLqbsYH8e1MhqNyR8=","aunts":["rx5b8PibXfRgtLtixVPkQXAIbBwuq9JoLfxtod58cks=","RsQnzT8vYsQKo7aMmKxmxOZCxfE5Wx4VGmsWCeOMfj4=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":12,"leaf_hash":"K+tGy7zIhPrOEfo1QXvvSwCOQLYiFPA01qEL7GDCaNk=","aunts":["THuJbfV7zi9REfd6QtKusxyXM6wb0x6Yn/pmzS0xa+U=","p4wN4fCz9Uly3vPzBIV9VtNr/S6K7tJ4rqce3FphA24=","Vd+/K/dB6Ir2w6OW9NGzJQNEOeKYJopWxm6vUZdsvBQ=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":11,"end_row":12},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfH50iywzI2cN2jN1j+IJIWlUGphPTXgcKFXvnh6tTfH1","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfIhyWIsGMUG8IddkbswoOLS2PaF5yfMGRTrKHU3rdq2C","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfM95KDyIOJkb2a6tx+Pt6Ms3MFMgjjqIaTcHppPNxPh8","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfB1zYHvegdPbZ2qyKTb6JSjzley9TRBwYUGJSIPRjM+4","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfMEKU7Lq6n2uuYrNNkwYFeuwEE92Dlyg2iUPQmCWZhEZ","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfL9mHdGQx61PTBsC1nVs/XmeLFda98appo2yFLyOKmIQ","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfEgetlsx79bfYOsrc3qLmZu/IdR897dKU/Gr18cLV5A1","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfFYSSYH9hixlwblBACTfeurt1wsiEbqCemmPg3vws5S9","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfAfEaIDfVyH6t8Ylmt+6fH4UaJnlF8p2j9c9CxBLBu1m","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfAz1+Z7WFGrHBOZXSJx9Wj1KEbWM+kzvzigR/TMwl0xt","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfKf96FaPM9Zj0CjZlxxLKCIxLyyKOEf1WpveMGeXlFLi","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfCzMTX+Ce/N58kVeaUDUdDY983PJ9aWBGcI2Heim1Bre","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLK2p/LVXONsbLzZrRgUo+uW2j1bXi6vPlbt94jORrMW","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfOuQD8GDNiFJWIdELplCVcsD3lU+Amw/TrHSygOH6Lqr","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfFuD+Q6YJ/PKW8dZYGqeQVar5uMRX6TtnRhaGVq13GBy","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfO0LUImuNaNqdisMhDzvmyHdzpjVONcRHNRjt3mrn+ga","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfOp4rKaycq4Pvq6GQgr4Ww/ZXbtbkEi4I9/0AyKUkCyf","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfP8QW46QVu5hSw6Xy59NLjc7dT5rdTRtXXiDO2zSSsbG","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLsAKHNSuF2HyoNlKbbgA8/BXtdFKcpnIQVv6o3FzREq","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLa8q4XZPC47IWKwH1qmfFmwFezEhTiDtmuDxSIuGs25","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfNDMwVLAxjRVJJvkzCl6kkBR/PVBYQ42+IX2MvWm+cOx","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfH9IexMaks64crpbpgfxOr3nv5V7QlWdI+sTWeG5b8CC","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfECsZ11XxgKLU1440wpCycy1rDjwd06ZohmO7N8qp0rv","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfIUWHPMPmh9Xj60xZWhbhoQsc1ImPJp8SyGk6LOTo3UG","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfFqvYTMd8App1jVSmDxezph5U/rjx9SRiBx1/prV6x7x","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfBLB52v3dpqCyfZMwDjQOeoxrfr6bqqbpOVpdfv+hiMx","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfDJgRRBRC54XvWc2Nz2jp23+4WLesR+U4tEGO7ClvaGW","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfMBA/0YnBIAENOWoWxGZdscIy0w+Hfbk5NKLwiVIHTCE","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfNcqj7I1n3grVKvX5ydiXLzorbm5Qn2HGA1ymzecza00","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfLY9XlzKP5kGy8rE/mH7ETJIy6AuEPLNOGbPpcErS5nL"],"subtree_root_proofs":[{"start":2,"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAAzD3FPBIMmNoiUMmWzTso2HbpAN6XeCB47azXrJ2snd4tX85wckwfgXQx","/////////////////////////////////////////////////////////////////////////////3QU2v3YBCNgsZp2axFECpWIT3Q/MYuYfaPCcOhBkIve"],"is_max_namespace_ignored":true},{"end":16,"nodes":["/////////////////////////////////////////////////////////////////////////////w721aysxkh3qg2sJilwwj0NIfz19tbdMJlwp2Q98B3j"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMw9xTwSDJjaIlAAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfPpTsEZuT4NBJf71KAKkxYHSDUXqC29CWgJ8NXLSNvY6","AAAAAAAAAAAAAAAAAAAAAAAAAO/4Mp8HNRdTT3wAAAAAAAAAAAAAAAAAAAAAAAAA7/gynwc1F1NPfN3m156KlGPp0ioirP7SOo7ZOD3sBgK5yWTaOk4OnkfZ"],"proofs":[{"total":64,"index":11,"leaf_hash":"y70vcVWrRBLSpVXzIpCyN1boiAhLqbsYH8e1MhqNyR8=","aunts":["rx5b8PibXfRgtLtixVPkQXAIbBwuq9JoLfxtod58cks=","RsQnzT8vYsQKo7aMmKxmxOZCxfE5Wx4VGmsWCeOMfj4=","TGLylje7Ozo2OOLHH0ZlEC56RlQYj7ONfy1P/2olb8k=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]},{"total":64,"index":12,"leaf_hash":"K+tGy7zIhPrOEfo1QXvvSwCOQLYiFPA01qEL7GDCaNk=","aunts":["THuJbfV7zi9REfd6QtKusxyXM6wb0x6Yn/pmzS0xa+U=","p4wN4fCz9Uly3vPzBIV9VtNr/S6K7tJ4rqce3FphA24=","Vd+/K/dB6Ir2w6OW9NGzJQNEOeKYJopWxm6vUZdsvBQ=","SkvBAf5D2IhGRcFn6hLu3cRIrmo5rWf5g/sNIq8Fj0c=","/rp149fn79ZfLIzWPf4pH7DK1QXOUiCZ4TRTpANuXqU=","Cp34Z6mufEkjg2fQcaJrwNsxX46zfqZ5zDM6AyyK6nk="]}],"start_row":11,"end_row":12},"namespace_version":0},"root":"vgyUsgeb2tbqQJiI5+RYFj33LrES4PCwx3vUQyLmozQ=","sub_threshold":64}] \ No newline at end of file diff --git a/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e31303020736861726573.json b/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e31303020736861726573.json new file mode 100755 index 0000000000..573c5907e2 --- /dev/null +++ b/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e31303020736861726573.json @@ -0,0 +1 @@ +[{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW6UkG7yVG5lH2IyaFgpyxhqUmHoPOdYHF32ywK46em6V","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW38l9Ot22iFg5MnrHeYDvoh9JxaNi8BU4pnwUxaLpn7X","/////////////////////////////////////////////////////////////////////////////+49pVTAhozVIRskdluL6zk9BeI0Afede8+lfgWK8FYJ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////5LDUQQTkNiXhf7vUpUH7ou4n8g2mM79FCnRPEJKUKHb"],"is_max_namespace_ignored":true},{"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl50oSNkw7HDAD3a6TXUbP2CDXmuhw5pl89P38jH/ZQrDE","AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl5x3ncVZLEYIE5Iu6y2l09BOo1i9drEYEu4YBjXDuGepU","/////////////////////////////////////////////////////////////////////////////wzjCDWSpV336xAFMv57XIdwCmdlzC+F/C2I7NU3GNZ6"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyLafis1/ladctqljgC9r7DahjKGlm1Tzj3TfM+fz8+xJ","AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyDHQGov+czQeCrjF+CJz4ekBuFsNgVhbKwpNjn9QlxPf","AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl53SWOmTizZ4VN/Sb+ZMpsVqBoOGOypdREQ8oNtehLfXQ"],"proofs":[{"total":256,"index":12,"leaf_hash":"mQtewTTT/WpWKzuEkDZ5+GEzFTZcDIdq1FFKYlbV2LM=","aunts":["kZnYgIEt/5c4OzxhxkJGWxwUewCys+Bv6L3aQhrxq5s=","LL3a2ZQMN4krCEFcp+6C1E6PARqr9onuEio82DGWC1Y=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":13,"leaf_hash":"kZnYgIEt/5c4OzxhxkJGWxwUewCys+Bv6L3aQhrxq5s=","aunts":["mQtewTTT/WpWKzuEkDZ5+GEzFTZcDIdq1FFKYlbV2LM=","LL3a2ZQMN4krCEFcp+6C1E6PARqr9onuEio82DGWC1Y=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":14,"leaf_hash":"RVSXMx/tH5izBUTch3iI1P158u+WnF6cMoMhQKnBbhM=","aunts":["aqaFBO75pIvkeMqk2sOH5fYdKx2lQZoItOXJr6jCYAY=","aF1am01WvyQp69iM78muUuBRv2K3wKQXotNvsDsk80k=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":12,"end_row":14},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW6UkG7yVG5lH2IyaFgpyxhqUmHoPOdYHF32ywK46em6V","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW38l9Ot22iFg5MnrHeYDvoh9JxaNi8BU4pnwUxaLpn7X","/////////////////////////////////////////////////////////////////////////////+49pVTAhozVIRskdluL6zk9BeI0Afede8+lfgWK8FYJ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////5LDUQQTkNiXhf7vUpUH7ou4n8g2mM79FCnRPEJKUKHb"],"is_max_namespace_ignored":true},{"end":16,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl50oSNkw7HDAD3a6TXUbP2CDXmuhw5pl89P38jH/ZQrDE","AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl5x3ncVZLEYIE5Iu6y2l09BOo1i9drEYEu4YBjXDuGepU","/////////////////////////////////////////////////////////////////////////////wzjCDWSpV336xAFMv57XIdwCmdlzC+F/C2I7NU3GNZ6"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyLafis1/ladctqljgC9r7DahjKGlm1Tzj3TfM+fz8+xJ","AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyDHQGov+czQeCrjF+CJz4ekBuFsNgVhbKwpNjn9QlxPf","AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl53SWOmTizZ4VN/Sb+ZMpsVqBoOGOypdREQ8oNtehLfXQ"],"proofs":[{"total":256,"index":12,"leaf_hash":"mQtewTTT/WpWKzuEkDZ5+GEzFTZcDIdq1FFKYlbV2LM=","aunts":["kZnYgIEt/5c4OzxhxkJGWxwUewCys+Bv6L3aQhrxq5s=","LL3a2ZQMN4krCEFcp+6C1E6PARqr9onuEio82DGWC1Y=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":13,"leaf_hash":"kZnYgIEt/5c4OzxhxkJGWxwUewCys+Bv6L3aQhrxq5s=","aunts":["mQtewTTT/WpWKzuEkDZ5+GEzFTZcDIdq1FFKYlbV2LM=","LL3a2ZQMN4krCEFcp+6C1E6PARqr9onuEio82DGWC1Y=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":14,"leaf_hash":"RVSXMx/tH5izBUTch3iI1P158u+WnF6cMoMhQKnBbhM=","aunts":["aqaFBO75pIvkeMqk2sOH5fYdKx2lQZoItOXJr6jCYAY=","aF1am01WvyQp69iM78muUuBRv2K3wKQXotNvsDsk80k=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":12,"end_row":14},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":62,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERx5J4PE8Hdj2bSwoIggd03pUGrroROgLmloHqTvLYIB4","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER4jYDS9z41siSkXQy9Wg/+w8YIyZ6XTFZeZCoD9+h9WS","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER9hIguNLSF8JUPJBg+u1ssn7vTiD0XsPiasAoU9qZurs","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER/exYK9yWUUN4YdjItC8pEl7XhSIluLW0DXSWfRbdC4c","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER1huoIEZzhJFS769LzP0rSoGeS4cQ3prLbHbC54QLfhG","/////////////////////////////////////////////////////////////////////////////8O+SajI2xwhhMJGl2moxMOp5t2H9ylDuKEE8o9dWIT0"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////wzdBApco90lQuj8WIlzEYF8dPoj8DOYzi54CRBSje4U"],"is_max_namespace_ignored":true},{"end":40,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyIhBAr15ki4QV0Ur8+kdTRXmmOqrTM/fAMae3Zpr7dcF","AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyKk1lDbsE2TyTX8HiLEZBEuOu5wiGtrsWhIJsFwsjL9/","/////////////////////////////////////////////////////////////////////////////+49pVTAhozVIRskdluL6zk9BeI0Afede8+lfgWK8FYJ"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfWw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW9GtdzH+G6ZWnXCvmOMgDySYngY8zHEBj1TxDpET5qvN","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW2faMGQNkVCmewuM6q5UVRRc2rq7s51NfpHQdBdnaCCf","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyLafis1/ladctqljgC9r7DahjKGlm1Tzj3TfM+fz8+xJ"],"proofs":[{"total":256,"index":10,"leaf_hash":"Op+QcMoeS8DBK530Pgjti7YbiwXv16iwBTFMRAbhyCA=","aunts":["Ju62CtSEfSpnoJ2n5USN81uL+yqz9g2rTWkUAWQ47As=","tgvDqpsZzHBMn6vhkvy7m7/iXUDLGopiW9u4hZ9oyLs=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":11,"leaf_hash":"Ju62CtSEfSpnoJ2n5USN81uL+yqz9g2rTWkUAWQ47As=","aunts":["Op+QcMoeS8DBK530Pgjti7YbiwXv16iwBTFMRAbhyCA=","tgvDqpsZzHBMn6vhkvy7m7/iXUDLGopiW9u4hZ9oyLs=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":12,"leaf_hash":"mQtewTTT/WpWKzuEkDZ5+GEzFTZcDIdq1FFKYlbV2LM=","aunts":["kZnYgIEt/5c4OzxhxkJGWxwUewCys+Bv6L3aQhrxq5s=","LL3a2ZQMN4krCEFcp+6C1E6PARqr9onuEio82DGWC1Y=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":10,"end_row":12},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":62,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERx5J4PE8Hdj2bSwoIggd03pUGrroROgLmloHqTvLYIB4","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER4jYDS9z41siSkXQy9Wg/+w8YIyZ6XTFZeZCoD9+h9WS","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER9hIguNLSF8JUPJBg+u1ssn7vTiD0XsPiasAoU9qZurs","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER/exYK9yWUUN4YdjItC8pEl7XhSIluLW0DXSWfRbdC4c","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER1huoIEZzhJFS769LzP0rSoGeS4cQ3prLbHbC54QLfhG","/////////////////////////////////////////////////////////////////////////////8O+SajI2xwhhMJGl2moxMOp5t2H9ylDuKEE8o9dWIT0"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////wzdBApco90lQuj8WIlzEYF8dPoj8DOYzi54CRBSje4U"],"is_max_namespace_ignored":true},{"end":40,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyIhBAr15ki4QV0Ur8+kdTRXmmOqrTM/fAMae3Zpr7dcF","AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyKk1lDbsE2TyTX8HiLEZBEuOu5wiGtrsWhIJsFwsjL9/","/////////////////////////////////////////////////////////////////////////////+49pVTAhozVIRskdluL6zk9BeI0Afede8+lfgWK8FYJ"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfWw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW9GtdzH+G6ZWnXCvmOMgDySYngY8zHEBj1TxDpET5qvN","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW2faMGQNkVCmewuM6q5UVRRc2rq7s51NfpHQdBdnaCCf","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyLafis1/ladctqljgC9r7DahjKGlm1Tzj3TfM+fz8+xJ"],"proofs":[{"total":256,"index":10,"leaf_hash":"Op+QcMoeS8DBK530Pgjti7YbiwXv16iwBTFMRAbhyCA=","aunts":["Ju62CtSEfSpnoJ2n5USN81uL+yqz9g2rTWkUAWQ47As=","tgvDqpsZzHBMn6vhkvy7m7/iXUDLGopiW9u4hZ9oyLs=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":11,"leaf_hash":"Ju62CtSEfSpnoJ2n5USN81uL+yqz9g2rTWkUAWQ47As=","aunts":["Op+QcMoeS8DBK530Pgjti7YbiwXv16iwBTFMRAbhyCA=","tgvDqpsZzHBMn6vhkvy7m7/iXUDLGopiW9u4hZ9oyLs=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":12,"leaf_hash":"mQtewTTT/WpWKzuEkDZ5+GEzFTZcDIdq1FFKYlbV2LM=","aunts":["kZnYgIEt/5c4OzxhxkJGWxwUewCys+Bv6L3aQhrxq5s=","LL3a2ZQMN4krCEFcp+6C1E6PARqr9onuEio82DGWC1Y=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":10,"end_row":12},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":62,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnuukbPjeqipgtONterRv6i5vHbyqgXfc/adJ56ptkgA7","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWngr0wr+BgsvYpQDhG/c0DYugXnt52aww8OrvWbLbDWUf","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnp2htvMoVXCckm5HlQ3y1QCmaqXt3Ewk+CgeDywFXvUN","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnp9JWayUkH5wWVgIUzbqT7wzPcvf76msf0hPo1o3AOVI","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnniv9rxP9q0CUW7XANSPNg3eqDhRxVh+Z4CcDuNhKGjL","/////////////////////////////////////////////////////////////////////////////5fzYdFs3Vsl0WPfb1fyZYY6bcHM0XAlB9ucSAMZvX1i"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3VIVqZkSK0uESat4t8rBm5ypm9/OJAorR5odvG3G8SP"],"is_max_namespace_ignored":true},{"end":42,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BmWqaTGfOWPZVQxv+W6crgT66qn1ATldKqXZ/RwcjLli","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BkVytVjG2iNZEKN99NZZIlKVGQtfzN3r/GskPCrj+34d","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bj1tOTbRTNt5Zp1cS0almVwY0Aui2+vIaCt6S2M23U/J","/////////////////////////////////////////////////////////////////////////////xMiHGZgvmljQTwaTRq9CPPmjKgEv83Eqic8234bCbwF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pt7bEp7bt4YO1mfyBSFqkjGtg8keb8dkmwNGXaXn56sg","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pukiDJW9g20G2VNOm0/9mFrs/oLiiGzaJRxqMloHwnar","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bse3dSwJc8yhF5TGDq626xQGp1/xw0hq8a0mJp4gOLvl"],"proofs":[{"total":256,"index":1,"leaf_hash":"Gwr25N3u3bSQOcxMyuWOVaaPGjbYh2n5TPO5TxxQlak=","aunts":["BouiICsYbdMTLn3ww8S10t+hTpArmblzoJ3AlR73qZE=","YDoo8UWQTciYuuxfzjUhyFFxlRDwzytcIFryWhr+t6U=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":2,"leaf_hash":"90m0xA3qn7xoqiU3bYWMKokfMmxh+8t2RPXqKXlFGYY=","aunts":["mslppPgA7eyD6n3h8a0rTObSz4YDdCsy5qCFZ8VfeVo=","rO6aXvtUMKMnLuv8PKog8KG2YepGGDDKBvvb86e3dkY=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":3,"leaf_hash":"mslppPgA7eyD6n3h8a0rTObSz4YDdCsy5qCFZ8VfeVo=","aunts":["90m0xA3qn7xoqiU3bYWMKokfMmxh+8t2RPXqKXlFGYY=","rO6aXvtUMKMnLuv8PKog8KG2YepGGDDKBvvb86e3dkY=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":1,"end_row":3},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":62,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnuukbPjeqipgtONterRv6i5vHbyqgXfc/adJ56ptkgA7","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWngr0wr+BgsvYpQDhG/c0DYugXnt52aww8OrvWbLbDWUf","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnp2htvMoVXCckm5HlQ3y1QCmaqXt3Ewk+CgeDywFXvUN","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnp9JWayUkH5wWVgIUzbqT7wzPcvf76msf0hPo1o3AOVI","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnniv9rxP9q0CUW7XANSPNg3eqDhRxVh+Z4CcDuNhKGjL","/////////////////////////////////////////////////////////////////////////////5fzYdFs3Vsl0WPfb1fyZYY6bcHM0XAlB9ucSAMZvX1i"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3VIVqZkSK0uESat4t8rBm5ypm9/OJAorR5odvG3G8SP"],"is_max_namespace_ignored":true},{"end":42,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BmWqaTGfOWPZVQxv+W6crgT66qn1ATldKqXZ/RwcjLli","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BkVytVjG2iNZEKN99NZZIlKVGQtfzN3r/GskPCrj+34d","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bj1tOTbRTNt5Zp1cS0almVwY0Aui2+vIaCt6S2M23U/J","/////////////////////////////////////////////////////////////////////////////xMiHGZgvmljQTwaTRq9CPPmjKgEv83Eqic8234bCbwF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pt7bEp7bt4YO1mfyBSFqkjGtg8keb8dkmwNGXaXn56sg","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pukiDJW9g20G2VNOm0/9mFrs/oLiiGzaJRxqMloHwnar","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bse3dSwJc8yhF5TGDq626xQGp1/xw0hq8a0mJp4gOLvl"],"proofs":[{"total":256,"index":1,"leaf_hash":"Gwr25N3u3bSQOcxMyuWOVaaPGjbYh2n5TPO5TxxQlak=","aunts":["BouiICsYbdMTLn3ww8S10t+hTpArmblzoJ3AlR73qZE=","YDoo8UWQTciYuuxfzjUhyFFxlRDwzytcIFryWhr+t6U=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":2,"leaf_hash":"90m0xA3qn7xoqiU3bYWMKokfMmxh+8t2RPXqKXlFGYY=","aunts":["mslppPgA7eyD6n3h8a0rTObSz4YDdCsy5qCFZ8VfeVo=","rO6aXvtUMKMnLuv8PKog8KG2YepGGDDKBvvb86e3dkY=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":3,"leaf_hash":"mslppPgA7eyD6n3h8a0rTObSz4YDdCsy5qCFZ8VfeVo=","aunts":["90m0xA3qn7xoqiU3bYWMKokfMmxh+8t2RPXqKXlFGYY=","rO6aXvtUMKMnLuv8PKog8KG2YepGGDDKBvvb86e3dkY=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":1,"end_row":3},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":28,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYD9nsnuas7ookYjtUG2IBBYv9KhDaNQ+tamYzhTY+6OM","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYJx5kCjbnWeZ6/slFFemjBRhp2Aq9W9cobSmaiR1sBw+","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYH9lCuwER0Ht1OKn9IWhZq/A8LoDuz7Od7wpDWGcUomT","/////////////////////////////////////////////////////////////////////////////zae4K60ljczTrfcWqsJJUF0MTKwqFfFXKBVL0td7DjY"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////4+bF90tO+bhqQOzJjag6BvVhSJipARNCnbJCTycD1aH"],"is_max_namespace_ignored":true},{"end":10,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+lq0GOq4a/AdHLFzsaelQnkAwTDbwo2ji/pZWx13iMT","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+j95jnL5vn7qDU3o2eAhptkorwU4qA8yvb/3FiL67L1","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER3LKY/6PkrSdYTheROSTztil4j6yG9qM0Urvl/4pvPSO","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER38g4HxU1PRlCQF31TFSAzIyHnvfCVFcY6856ByGlxvc","/////////////////////////////////////////////////////////////////////////////1M85hXXuJ1f1RpoLD7kLUz9uhKCeQI1t1fBlPw4G0DF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGFMuHLuVTHzjuOATq/TxXpQC9Q1EO7OCS899J3rEsM0","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPAqFEaYzSAYn9Smj+qMqvSdHKi5gn5SigBCkdNvhTqev","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER5YVhwMm3wCuQ5ZHawGbdFxHZWEWF0AwpsvwttXqsJb7"],"proofs":[{"total":256,"index":7,"leaf_hash":"tVGwaXfbSRaVo4uIJgWbGr6qHLHwOFBfSUT52A2n/VY=","aunts":["7QvB8xAffZakHWJq7aDOxxpQK7pDb+UOUJiNbisUf1A=","znxriAva5sji5Q5LT5Na1UGxDGBaURsCzt1ViXuDzKg=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":8,"leaf_hash":"+NYxJU2YBHXSotB+V4axkfXipuVT4ZEO1n/PD2rYjBc=","aunts":["pTBe3dLl5BcV8J2NF9gChjtvPaZz9yx73ea1AGCMv4g=","A0toYQnDj8xrTlv2zQ8+zb2J5h85M04fcSeCqHfuGLY=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":9,"leaf_hash":"pTBe3dLl5BcV8J2NF9gChjtvPaZz9yx73ea1AGCMv4g=","aunts":["+NYxJU2YBHXSotB+V4axkfXipuVT4ZEO1n/PD2rYjBc=","A0toYQnDj8xrTlv2zQ8+zb2J5h85M04fcSeCqHfuGLY=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":7,"end_row":9},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":28,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYD9nsnuas7ookYjtUG2IBBYv9KhDaNQ+tamYzhTY+6OM","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYJx5kCjbnWeZ6/slFFemjBRhp2Aq9W9cobSmaiR1sBw+","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYH9lCuwER0Ht1OKn9IWhZq/A8LoDuz7Od7wpDWGcUomT","/////////////////////////////////////////////////////////////////////////////zae4K60ljczTrfcWqsJJUF0MTKwqFfFXKBVL0td7DjY"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////4+bF90tO+bhqQOzJjag6BvVhSJipARNCnbJCTycD1aH"],"is_max_namespace_ignored":true},{"end":10,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+lq0GOq4a/AdHLFzsaelQnkAwTDbwo2ji/pZWx13iMT","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+j95jnL5vn7qDU3o2eAhptkorwU4qA8yvb/3FiL67L1","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER3LKY/6PkrSdYTheROSTztil4j6yG9qM0Urvl/4pvPSO","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER38g4HxU1PRlCQF31TFSAzIyHnvfCVFcY6856ByGlxvc","/////////////////////////////////////////////////////////////////////////////1M85hXXuJ1f1RpoLD7kLUz9uhKCeQI1t1fBlPw4G0DF"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGFMuHLuVTHzjuOATq/TxXpQC9Q1EO7OCS899J3rEsM0","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPAqFEaYzSAYn9Smj+qMqvSdHKi5gn5SigBCkdNvhTqev","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER5YVhwMm3wCuQ5ZHawGbdFxHZWEWF0AwpsvwttXqsJb7"],"proofs":[{"total":256,"index":7,"leaf_hash":"tVGwaXfbSRaVo4uIJgWbGr6qHLHwOFBfSUT52A2n/VY=","aunts":["7QvB8xAffZakHWJq7aDOxxpQK7pDb+UOUJiNbisUf1A=","znxriAva5sji5Q5LT5Na1UGxDGBaURsCzt1ViXuDzKg=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":8,"leaf_hash":"+NYxJU2YBHXSotB+V4axkfXipuVT4ZEO1n/PD2rYjBc=","aunts":["pTBe3dLl5BcV8J2NF9gChjtvPaZz9yx73ea1AGCMv4g=","A0toYQnDj8xrTlv2zQ8+zb2J5h85M04fcSeCqHfuGLY=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":9,"leaf_hash":"pTBe3dLl5BcV8J2NF9gChjtvPaZz9yx73ea1AGCMv4g=","aunts":["+NYxJU2YBHXSotB+V4axkfXipuVT4ZEO1n/PD2rYjBc=","A0toYQnDj8xrTlv2zQ8+zb2J5h85M04fcSeCqHfuGLY=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":7,"end_row":9},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":16,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyBT9mosvOga71GHDC4X/EdOzr10r39conX28tNHpsa0J","/////////////////////////////////////////////////////////////////////////////wzjCDWSpV336xAFMv57XIdwCmdlzC+F/C2I7NU3GNZ6"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////KJ6uuwXvUmlspK4eKWfaDvdjoMs2YXp5NSynYy1FnB"],"is_max_namespace_ignored":true},{"end":1,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl50vP5WjXyD/a4Xg6PscDPQWYZUHqsXEe7J/zVDFbNjak","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYVmzb7vYGBR4JLm1OWyes9X+fxhLhm5MotdshVGyhvqQ","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYVBbMC5kdtsKPvTmvZJ5wqsDcLd3v/gCn5b0iqKatxrt","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYbG9nLLchOxUboqjOwUNglPLZnOlOYbCpHzDLb4BdHLS","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYfq9WwxPrHAy0Lj54/6v+7OYc+5mmh1YpIKTQwjNxUXn","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYRi5IBUWWkKX1Bhu2cewXsKtDf6Po5xPXnwN7dJLecFj","/////////////////////////////////////////////////////////////////////////////wP33atuVCjRFfbMKQ81ItA2GMgJoaLD/qQyLaG/TM9h"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl5w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl53SWOmTizZ4VN/Sb+ZMpsVqBoOGOypdREQ8oNtehLfXQ","AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl55wptXjwzO+B9GbLWNF83/QSwLiH1Hqq03Y4rmoiW/7w","AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYTt+Ee2FqqJqIs1ebK44c0IyYX7btGyymz4ZAfeNSGMb"],"proofs":[{"total":256,"index":14,"leaf_hash":"RVSXMx/tH5izBUTch3iI1P158u+WnF6cMoMhQKnBbhM=","aunts":["aqaFBO75pIvkeMqk2sOH5fYdKx2lQZoItOXJr6jCYAY=","aF1am01WvyQp69iM78muUuBRv2K3wKQXotNvsDsk80k=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":15,"leaf_hash":"aqaFBO75pIvkeMqk2sOH5fYdKx2lQZoItOXJr6jCYAY=","aunts":["RVSXMx/tH5izBUTch3iI1P158u+WnF6cMoMhQKnBbhM=","aF1am01WvyQp69iM78muUuBRv2K3wKQXotNvsDsk80k=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":16,"leaf_hash":"uuaVU96dx286rQ8bIzW7GlXCqNW5DNy5ZOaWshe3yDw=","aunts":["4XoaH71nf3OK/TcGqG4+Lf9SwoG9SwAttrPCCeO/3uQ=","GCgBzrYrTrKvoB66cGffHYlFLQpYNGj2CPTng+xPLt0=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","CCX/QVcnImltJR7SKm+zI8Jd67C4pdqsK/7cq+YUtRU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":14,"end_row":16},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":16,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA4GELUDfAv/pYyBT9mosvOga71GHDC4X/EdOzr10r39conX28tNHpsa0J","/////////////////////////////////////////////////////////////////////////////wzjCDWSpV336xAFMv57XIdwCmdlzC+F/C2I7NU3GNZ6"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////KJ6uuwXvUmlspK4eKWfaDvdjoMs2YXp5NSynYy1FnB"],"is_max_namespace_ignored":true},{"end":1,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl50vP5WjXyD/a4Xg6PscDPQWYZUHqsXEe7J/zVDFbNjak","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYVmzb7vYGBR4JLm1OWyes9X+fxhLhm5MotdshVGyhvqQ","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYVBbMC5kdtsKPvTmvZJ5wqsDcLd3v/gCn5b0iqKatxrt","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYbG9nLLchOxUboqjOwUNglPLZnOlOYbCpHzDLb4BdHLS","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYfq9WwxPrHAy0Lj54/6v+7OYc+5mmh1YpIKTQwjNxUXn","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmEAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYRi5IBUWWkKX1Bhu2cewXsKtDf6Po5xPXnwN7dJLecFj","/////////////////////////////////////////////////////////////////////////////wP33atuVCjRFfbMKQ81ItA2GMgJoaLD/qQyLaG/TM9h"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl5w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOBhC1A3wL/6WMgAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl53SWOmTizZ4VN/Sb+ZMpsVqBoOGOypdREQ8oNtehLfXQ","AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl55wptXjwzO+B9GbLWNF83/QSwLiH1Hqq03Y4rmoiW/7w","AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYTt+Ee2FqqJqIs1ebK44c0IyYX7btGyymz4ZAfeNSGMb"],"proofs":[{"total":256,"index":14,"leaf_hash":"RVSXMx/tH5izBUTch3iI1P158u+WnF6cMoMhQKnBbhM=","aunts":["aqaFBO75pIvkeMqk2sOH5fYdKx2lQZoItOXJr6jCYAY=","aF1am01WvyQp69iM78muUuBRv2K3wKQXotNvsDsk80k=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":15,"leaf_hash":"aqaFBO75pIvkeMqk2sOH5fYdKx2lQZoItOXJr6jCYAY=","aunts":["RVSXMx/tH5izBUTch3iI1P158u+WnF6cMoMhQKnBbhM=","aF1am01WvyQp69iM78muUuBRv2K3wKQXotNvsDsk80k=","TCVa0qvBJOsBlID1W7c56xvMvKOp+Qu/6zWfPkqJod8=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":16,"leaf_hash":"uuaVU96dx286rQ8bIzW7GlXCqNW5DNy5ZOaWshe3yDw=","aunts":["4XoaH71nf3OK/TcGqG4+Lf9SwoG9SwAttrPCCeO/3uQ=","GCgBzrYrTrKvoB66cGffHYlFLQpYNGj2CPTng+xPLt0=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","CCX/QVcnImltJR7SKm+zI8Jd67C4pdqsK/7cq+YUtRU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":14,"end_row":16},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":10,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPAWZlLf12zeB8y19MrLF9auAWZJo//G0C/yIQICA2W8O","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGakAPK40gMY6oUbmur78PY3rRgTaCJhApm3LsQ3mmSH","/////////////////////////////////////////////////////////////////////////////1M85hXXuJ1f1RpoLD7kLUz9uhKCeQI1t1fBlPw4G0DF"],"is_max_namespace_ignored":true},{"end":61,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER++O1GNScI1bAsUZXsujQREdKZt87G5h1tmCkousCYyx","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW78jMorL+sD69iD/EtNa4NxNN2999j3+eH8ZDzjemx6t","/////////////////////////////////////////////////////////////////////////////8O+SajI2xwhhMJGl2moxMOp5t2H9ylDuKEE8o9dWIT0"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER5YVhwMm3wCuQ5ZHawGbdFxHZWEWF0AwpsvwttXqsJb7","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW9GtdzH+G6ZWnXCvmOMgDySYngY8zHEBj1TxDpET5qvN"],"proofs":[{"total":256,"index":9,"leaf_hash":"pTBe3dLl5BcV8J2NF9gChjtvPaZz9yx73ea1AGCMv4g=","aunts":["+NYxJU2YBHXSotB+V4axkfXipuVT4ZEO1n/PD2rYjBc=","A0toYQnDj8xrTlv2zQ8+zb2J5h85M04fcSeCqHfuGLY=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":10,"leaf_hash":"Op+QcMoeS8DBK530Pgjti7YbiwXv16iwBTFMRAbhyCA=","aunts":["Ju62CtSEfSpnoJ2n5USN81uL+yqz9g2rTWkUAWQ47As=","tgvDqpsZzHBMn6vhkvy7m7/iXUDLGopiW9u4hZ9oyLs=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":9,"end_row":10},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+lq0GOq4a/AdHLFzsaelQnkAwTDbwo2ji/pZWx13iMT","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER6EUvDxT8O8LCQBFhTOPLjpjiissQRib2LWmGsF7r9Fl","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERzyhrs5pExUS2tgCispiXSrxtv1Qj/cznkDIzocX/YqQ","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERymlAJ1D5lmUua98pbp1k5WwD2pQD36RIefo17ka3kjn","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER7sBLsALkFp87ya6QzsJuETDYcgk4/LoXdzjcPqokKA5","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER0HLMyOFI4cIBvtfE7Pl8fYhrmy9/sZIUEQpRLXIOAav","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+WOCwOMO2Z4H/gSJOjgCbBn3uS95jhbinFq2iYvzy/n","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER34YzUbc6X8onZk6/OhwYqhAE/pGCG1fm3dHgMktQO+8","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERwaZFn1wy+ydaRwqCbplcRgY7wS2u/lQRzjOx1hPK/wX","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERzj9rqZ8TOTAIMOZ9Ff9reLzfGPhzH3Dyc/PI/ngk4E7","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER2ZNaFpdyN0ht2luLGgkNYzughpOacNbZgA826faUmFL","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+ABlnn0WxPpGJqG25Cy5ziTZ3ggG7rknL4sls6sDMyN","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERzmoZrWc3pmkuQsFnbg5e3PMfj3B6c/ZVDJRqJbtp5OY","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER2YTECuvUO33Xs4crP776FiUwwmfKK11MOGjig/IJ8Jj","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERxMq7ic2vNSeMYjX3pBXr0jgwwppqQSm+j4dVrvqkwDL","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER5oxU9GM5GI82RxrFPFPlxXf3loKbZzNVEcHc7aHrWF1","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER0grvQdPCDbIwrcyqbhSVGX1J9HtZevtuviCRnRPq2xe","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER/4qyQ4tZF491S7/wfc33RVqI8t/LXLoOOlMYgeGFAwL","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERzOavD07qHcFTWTc1Gk8mkxhKMsxO6PvpuoRpwUQ4JKd","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER4nAyzCqZM0K8YZZRKyVzHZbQFG6QwxR/OU933RClhcv","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER0famZWVVcHJcT8AM0YTBGOCukSL1r/0e0Bg7zXa4NT/","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER63KGcdX4aIt7VFLuDfAgAMwAUPtcjFTcOqv1L880+U2","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERwfG87cPQ2tTzWU+zdqT1ncKsZCdLQRgVGbnDdelIxeN","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER3V1KN9icoI7pWGhdLKrRrV+bx21L6ICCVDOe/Cf0E/s","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER12MMCPhnIkGCWZNmXK/FobNnR3JBUHzjikNsWmnyNDv","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER0OP4wxccl7jhSyvhWloytfPM6depWXw4j8ANIr4W2mv","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER8Z2DEGhGfCIlKAQeSB6Vw6sQFZZbpyUqoMYM6/IKnZi","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER2BUF64Ecv6OY9Zu7NlX2HXcsJ/u4dttY61IXHYhGftl","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+YHpP+sjdqPhAp6EBDsFAe1A+oGHmPc1EzmEfArXjSc","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER4YVqSxKIE0fBbCtuxXIdUVI5V8QTyjXlO/h0LND8ssq","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER58E/skN+KZxzuQHVXjIKXdIcpwgeNXyrmzq+ivtUElh","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER29CC+5bo6arIaKp3sznaOwoRlVjaVa60+Kiz+mSOG4I","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER3MICTPhXGs4fYV/B2FK4Gf86empopBhtJbZoPDcfU/8","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERxlvLzdSfOPO+A2A6RMKas6AHD4AGJHJ9f9qTKggo0YG","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+9J2kUHTdwi9ICsBJXiteBmUxt+WEz48AeFqM8FN5yz","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER/pUxU+W56ZUzNMZH/VxVkANCa+mtBG7Q7kOcMRMvWhf","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+14R1tmvSFW/6C8iX5OL0C/7aXKKHV5rU2yMNBmwrAO","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER0gunjsQgaw27MZmC7erm3rQR/vyBh2wf9CiUWhVFZBC","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER1+VhrRkmH0MUzmcLt6mVQvxH8OSgD7HiHcF8dTRHln+","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER/0gPyQlkFI0wqbbkclEyJtl012e244sVybqShWyKTc4","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER5UC1UEYmvZQO4Y0vqB48y3Ebf/BuvLRnAnDnruo1wWh","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERySHkLmc27M0qix8znFhZzkYJzGl0LgLzKNbKCaAZTOG","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER/aIyp4/khVL8LPaff0XFnmq+gAhZz7ZF0JBjUGz45LL","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER67V5MlBFT42tSS+afFL1KbE1zSF8f0+p86ekEj8qszx","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER3O98IFIXNLcfJ7nhKKZqdr5V22tVRUmCajwlp/galYM","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER5UEc236bSbOs9PheUfIl4zGCqK8OvA32lSs2jKLIPNM","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERx7xgbv7w02p9Cv9aI8teDyJIcxhyqePpNzK1uuYSO6z","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER0wZBZStIzzIhqCbh5a1pFN7nkJOBCPkRKrL/LFJjVfd","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERzHDehZzXx4NZiRfGS0Wfeo22r7L4PfxCouFoeTTkmq7","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER6SRZgvTOwqJBUqn0/b8IMa7fuHOoX84ltKy+YNLf1w0","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERyL4x0axNxxe3hl8MvqEf09KlcpAyv9iHJgspVJIdSpH","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER3M3QNsibBwwNVpKzTck8BmFL+0z/gcy+m9LczKPBDsK","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERwt5rSq4ZUztQDQyVWZkScivPu4gbU23oN205xQzR5kY","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+Jg9HSYTZ7H7euO3pQJ8ROYJOtBg/kb/PnUKUR1WMvZ","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERxXw16PtvXymCVy5BuE8Q6Ybcoh9xM87hjXPGDT8bH1L","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER78EIfhTPszRHXBkr76aiMrRk822goGqAlq/2jOix0UX","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER+yrwX+uod3iNJbWKAL+GsETIebpIbOed12RrdEG75sZ","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER2G9stRBlLQMMT3Gk7jj6XzO6AE+qlsHQwxhP0yX6WEC"],"subtree_root_proofs":[{"start":10,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPAWZlLf12zeB8y19MrLF9auAWZJo//G0C/yIQICA2W8O","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGakAPK40gMY6oUbmur78PY3rRgTaCJhApm3LsQ3mmSH","/////////////////////////////////////////////////////////////////////////////1M85hXXuJ1f1RpoLD7kLUz9uhKCeQI1t1fBlPw4G0DF"],"is_max_namespace_ignored":true},{"end":61,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER++O1GNScI1bAsUZXsujQREdKZt87G5h1tmCkousCYyx","AAAAAAAAAAAAAAAAAAAAAAAAAM51Zbzb+l5U31sAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW78jMorL+sD69iD/EtNa4NxNN2999j3+eH8ZDzjemx6t","/////////////////////////////////////////////////////////////////////////////8O+SajI2xwhhMJGl2moxMOp5t2H9ylDuKEE8o9dWIT0"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TERw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAcOfych/xq1TER5YVhwMm3wCuQ5ZHawGbdFxHZWEWF0AwpsvwttXqsJb7","AAAAAAAAAAAAAAAAAAAAAAAAAHDn8nIf8atUxEcAAAAAAAAAAAAAAAAAAAAAAAAAznVlvNv6XlTfW9GtdzH+G6ZWnXCvmOMgDySYngY8zHEBj1TxDpET5qvN"],"proofs":[{"total":256,"index":9,"leaf_hash":"pTBe3dLl5BcV8J2NF9gChjtvPaZz9yx73ea1AGCMv4g=","aunts":["+NYxJU2YBHXSotB+V4axkfXipuVT4ZEO1n/PD2rYjBc=","A0toYQnDj8xrTlv2zQ8+zb2J5h85M04fcSeCqHfuGLY=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":10,"leaf_hash":"Op+QcMoeS8DBK530Pgjti7YbiwXv16iwBTFMRAbhyCA=","aunts":["Ju62CtSEfSpnoJ2n5USN81uL+yqz9g2rTWkUAWQ47As=","tgvDqpsZzHBMn6vhkvy7m7/iXUDLGopiW9u4hZ9oyLs=","MxzeRQYS0qaKHodoBI5wBan7WobAz7aBXch2dKMCEVI=","C2pJbS3dqv3EnkJ8ZtNJlykcBAv4xJIsX7Vg4wUc1gU=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":9,"end_row":10},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/x5F2RCHOVFa+3AHKWREgPyw3WG1FJarOdCvHH2vnvVQ","/////////////////////////////////////////////////////////////////////////////ymRMRs9fz/0PbGYzk95u+7v5LwEcn4ji0EjistkwtQm"],"is_max_namespace_ignored":true},{"end":61,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnn7DVh6QHLEruaIE4VXnoZude+ykRUIgeM1kijKkxXvG","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pjjtWh7P8BVfXFxOfvVGR5AmJFdMk8UNcNRghLisS4ZF","/////////////////////////////////////////////////////////////////////////////5fzYdFs3Vsl0WPfb1fyZYY6bcHM0XAlB9ucSAMZvX1i"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWng==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnsloC40ez3lFNas4wbjxDbyqZr9R/WhJNs0iRZBjSxSI","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pt7bEp7bt4YO1mfyBSFqkjGtg8keb8dkmwNGXaXn56sg"],"proofs":[{"total":256,"leaf_hash":"BouiICsYbdMTLn3ww8S10t+hTpArmblzoJ3AlR73qZE=","aunts":["Gwr25N3u3bSQOcxMyuWOVaaPGjbYh2n5TPO5TxxQlak=","YDoo8UWQTciYuuxfzjUhyFFxlRDwzytcIFryWhr+t6U=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":1,"leaf_hash":"Gwr25N3u3bSQOcxMyuWOVaaPGjbYh2n5TPO5TxxQlak=","aunts":["BouiICsYbdMTLn3ww8S10t+hTpArmblzoJ3AlR73qZE=","YDoo8UWQTciYuuxfzjUhyFFxlRDwzytcIFryWhr+t6U=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"end_row":1},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/x5F2RCHOVFa+3AHKWREgPyw3WG1FJarOdCvHH2vnvVQ","/////////////////////////////////////////////////////////////////////////////ymRMRs9fz/0PbGYzk95u+7v5LwEcn4ji0EjistkwtQm"],"is_max_namespace_ignored":true},{"end":61,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnn7DVh6QHLEruaIE4VXnoZude+ykRUIgeM1kijKkxXvG","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pjjtWh7P8BVfXFxOfvVGR5AmJFdMk8UNcNRghLisS4ZF","/////////////////////////////////////////////////////////////////////////////5fzYdFs3Vsl0WPfb1fyZYY6bcHM0XAlB9ucSAMZvX1i"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWng==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAJLBwV+0XbhiWnsloC40ez3lFNas4wbjxDbyqZr9R/WhJNs0iRZBjSxSI","AAAAAAAAAAAAAAAAAAAAAAAAACSwcFftF24Ylp4AAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pt7bEp7bt4YO1mfyBSFqkjGtg8keb8dkmwNGXaXn56sg"],"proofs":[{"total":256,"leaf_hash":"BouiICsYbdMTLn3ww8S10t+hTpArmblzoJ3AlR73qZE=","aunts":["Gwr25N3u3bSQOcxMyuWOVaaPGjbYh2n5TPO5TxxQlak=","YDoo8UWQTciYuuxfzjUhyFFxlRDwzytcIFryWhr+t6U=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":1,"leaf_hash":"Gwr25N3u3bSQOcxMyuWOVaaPGjbYh2n5TPO5TxxQlak=","aunts":["BouiICsYbdMTLn3ww8S10t+hTpArmblzoJ3AlR73qZE=","YDoo8UWQTciYuuxfzjUhyFFxlRDwzytcIFryWhr+t6U=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"end_row":1},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":36,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bqte+ZQkbTA8HOu0CxjGCDeZup4ji+bGchdTpyr3Lrg4","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bn8tT/+1SOoJpeM1rmEeGCOgUgDKZtWy7ZF3UIWwxz4Z","/////////////////////////////////////////////////////////////////////////////zWOe5vJ0ndF1DmsdTXfBNmsBjnI2SK6LPKfX/HpAVgu"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////9V+kkTzzVvvFADt4L7wcQf7DGXEIlc1s4mzPUPEjKGn"],"is_max_namespace_ignored":true},{"end":27,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYD96DTI8cwT0eZz+iKcOdTPnHhl3IlA5APQx6dHTh8Y0","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPJWSscQ0vxND8R38UhrwI85jCQ0y/+F78pq32AXJEytq","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGCbAM3J153gZFdbqpgkRzNkLwqHTpWBn5Cd1NyzsiyJ","/////////////////////////////////////////////////////////////////////////////zae4K60ljczTrfcWqsJJUF0MTKwqFfFXKBVL0td7DjY"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYCRvav532xTB9wHf5Gk/RBVkqmCYDeFl/1z5H0i5j5Lt","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYNV+msVGzP03itdlO6yGY0V/niqN0ihTz8JzxGc2tiX5","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGFMuHLuVTHzjuOATq/TxXpQC9Q1EO7OCS899J3rEsM0"],"proofs":[{"total":256,"index":5,"leaf_hash":"pCw4CCNMveZWlFDa11ODcfggZK/THf/ZmMHtCjbYu6g=","aunts":["RmQYFxbaDZTrJGsRq0PwA7b4hPVT6iIb1KA8UQxLzXk=","2MRQ3/Txb8R44h+MPYGILPasJAYvfavF3gWETOr5Vh8=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":6,"leaf_hash":"7QvB8xAffZakHWJq7aDOxxpQK7pDb+UOUJiNbisUf1A=","aunts":["tVGwaXfbSRaVo4uIJgWbGr6qHLHwOFBfSUT52A2n/VY=","znxriAva5sji5Q5LT5Na1UGxDGBaURsCzt1ViXuDzKg=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":7,"leaf_hash":"tVGwaXfbSRaVo4uIJgWbGr6qHLHwOFBfSUT52A2n/VY=","aunts":["7QvB8xAffZakHWJq7aDOxxpQK7pDb+UOUJiNbisUf1A=","znxriAva5sji5Q5LT5Na1UGxDGBaURsCzt1ViXuDzKg=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":5,"end_row":7},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":36,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bqte+ZQkbTA8HOu0CxjGCDeZup4ji+bGchdTpyr3Lrg4","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bn8tT/+1SOoJpeM1rmEeGCOgUgDKZtWy7ZF3UIWwxz4Z","/////////////////////////////////////////////////////////////////////////////zWOe5vJ0ndF1DmsdTXfBNmsBjnI2SK6LPKfX/HpAVgu"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////9V+kkTzzVvvFADt4L7wcQf7DGXEIlc1s4mzPUPEjKGn"],"is_max_namespace_ignored":true},{"end":27,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYD96DTI8cwT0eZz+iKcOdTPnHhl3IlA5APQx6dHTh8Y0","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPJWSscQ0vxND8R38UhrwI85jCQ0y/+F78pq32AXJEytq","AAAAAAAAAAAAAAAAAAAAAAAAAFRPQJ+q7n+DaDwAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGCbAM3J153gZFdbqpgkRzNkLwqHTpWBn5Cd1NyzsiyJ","/////////////////////////////////////////////////////////////////////////////zae4K60ljczTrfcWqsJJUF0MTKwqFfFXKBVL0td7DjY"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYCRvav532xTB9wHf5Gk/RBVkqmCYDeFl/1z5H0i5j5Lt","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYNV+msVGzP03itdlO6yGY0V/niqN0ihTz8JzxGc2tiX5","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAAVE9An6ruf4NoPGFMuHLuVTHzjuOATq/TxXpQC9Q1EO7OCS899J3rEsM0"],"proofs":[{"total":256,"index":5,"leaf_hash":"pCw4CCNMveZWlFDa11ODcfggZK/THf/ZmMHtCjbYu6g=","aunts":["RmQYFxbaDZTrJGsRq0PwA7b4hPVT6iIb1KA8UQxLzXk=","2MRQ3/Txb8R44h+MPYGILPasJAYvfavF3gWETOr5Vh8=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":6,"leaf_hash":"7QvB8xAffZakHWJq7aDOxxpQK7pDb+UOUJiNbisUf1A=","aunts":["tVGwaXfbSRaVo4uIJgWbGr6qHLHwOFBfSUT52A2n/VY=","znxriAva5sji5Q5LT5Na1UGxDGBaURsCzt1ViXuDzKg=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":7,"leaf_hash":"tVGwaXfbSRaVo4uIJgWbGr6qHLHwOFBfSUT52A2n/VY=","aunts":["7QvB8xAffZakHWJq7aDOxxpQK7pDb+UOUJiNbisUf1A=","znxriAva5sji5Q5LT5Na1UGxDGBaURsCzt1ViXuDzKg=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":5,"end_row":7},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BmWqaTGfOWPZVQxv+W6crgT66qn1ATldKqXZ/RwcjLli","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BhmYBFhXhlEso5TknTXVl5tYArVDU2Gglu49A2hN9O57","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BjCMCE11HBRh74q+C0c2rPSX5AzzR+AvjPFvbBBMEDzn","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bo7ieV07RbWhHPhXo4TwRBdmlcOIqS4TxNSQnMerQiZ0","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BtotR8mBX91fQrMtc8UnuavITJNorsoBGyZOvS94FeYc","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BrPPf2Zv5a2x7dvXjYDQyykcA6RbAJ7XbdUHXcxtlZRw","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BgduCEx/mpEcc4taIPPWrgdBBj53Wui49c8/KSnYJLRu","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bg6lXe+Khoi5xshhpE6DbhJZTgYmhXwJHhUgSjLn8VcY","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BvBNthRLpcSiMtZvv11262nGfL1avP/lL/FGj9gnDFqU","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BiEN2+2uQMxv80Jkt2CKCm7VGHFPZgg+VwRMEdujppB5","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BrqtTqYG7s78YHBkZ/flnTkotT/s+18Y52hZTLVGS05J","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BlcZeH5/LBgV32SXw9jjuWCcyhjTz18Z01TDwnRl2SeY","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BnZfR/9/jkPtTkvkq2MJaX55VCMwyBa7tDYOAMWMlLUW","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bj5CquqPjKA5T4zUD9lkxduPfLBlR62DpnPVQwtNE/6t","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BnWEn2E9205Tt07IJr/BN+/u5/q1AUWqUv4iIohS+L9H","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BuQZL9fcjl4aXeRE0NfEUDJZ0VRnPymtkRlzPtMrWRRA","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BnM0O9SNh4ZmtegEKAqkKApY5Elc1CEdIBqZ0TdaS+Ll","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bs548p7yvB0DUOn4iOHg3a9jyZ8UE0jrlnc8alOPCjgu","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BiT7/fw2zH738c74VH3c0aNoKDMU8gBPj7+SLip423XQ","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bv7OHfB2FSdgolYl3UbbQE1ajBIP2J65yjdLBxJOY15O","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bo7wNApEd63SCqI1fC+Es6y3a/qoopLsEtAENbrLqy5D","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BiIzKuUBOCGdn0M1CiUZGP7wLygjytuTzk1IG/ruyiBB","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BoD7D31nbfpeSD5ggd25Cpts2QfW2O5tggwnEbGJRRWf","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bo7+L196zvVjMe/Ot0OIIPfoaZ8f3ehf0DgNB/qzz9zE","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BqEBRqu0jN8WcpjF9blas8Iz50a40wZqtQDJXzCJcIVE","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bm0Lxw9JEeuC7VAkVdF29hGbp5ROCqfgoq0OX4XYW+vc","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bh+G0uQEgrC/t30XzinRlwrNBYDzkUR3UYHz3rHXQYoy","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BtH+jR3wAGlhaRHNc453m+6lPdvqHD3wGINbcXqRX1hP","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BuQBNBOE702AQn7LNORUuGbMzb7p9G5w5XVPaeHlUTkL","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BvIJOSVFr4GS4H2RKuuZ9rEgDtqsCu/zEGInaOJwGU4R","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BhOPmbTzlX5UiXGm1t1SClWMGRvQMk1rOOps1wM2iY+c","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BmKtJCa+jLEsxG+Vma0Pd4Jyhl74+hwtnTS7NkXggMKQ","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bnu9CFCNXXBnUSrqnD19cLfSwCj0YRrxOWqM9/1SQXEq","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BloUxPkGwAq0TI+HxcmS09TIreCAwirmrtN7kk0OthHk","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BgYhjueOPD8Fd2NgLJD82dsp3xLqWPBIdNKDlBEq0CBu","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BoBQdY9EIjBeJL+wQqnlBEuu2LQA8xl7hPoS1pYk8/U+","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BpPcT8oImYf01sNulFy3OWK4kD12v1I+08HsvXrDhsKj","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BotiYQVbFQKPL1zVrFSjcHCTYnNmbjDbaajdHJhJjWPN","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Br16yB3e87iLPMmr5s0L/IZN+TOSJalV664hZWTIl5kP","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Btev0bVvctGbGKiG/F7UyUuUjY79y7pXEJ1unBE5QP/f","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bp+5X2SJG++P2374DRJhug5fW02Rwh8q+quTyO6TLyvn","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BnybUKNtYrC+RsDiZjvcusrpKTNuRkdU+Mv0EAfEJIcO","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BpBCxeDzdltCOA3NtfURsO1+70QxtISRA/6fXU8Vv0wW","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bkcsm5LDdFMyFEiioEqiU00BCkhqZ9NvQSfAQ6H/rKxC","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bv9N7JUkY52c9uJ8LsPAOawZVZqUmQ/pGsubWN0uQZtu","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BpEvQeyZVfstO9btI4QkL1rHcTqWb2WFqOWcJUq8vb57","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BjT/pCuonz2NDEqrLW9BGJAJV9pR1s13HhwYhbm1kH/9","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bgbuox+yOezs1uta+zBfA2GvWDt5884nJrYTZ9D2zAR/","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BhYtiXALsCJfYyx7l2LVk8ECsdhskAbrdFWVQrxcdoWe","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BunMsAdf1Vovb1rYCzKKbKfLdqX1f+lHhgS4WmTLUV5q","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BtKBOlNRBzTh3LPxQ/1NXgVXCX92xAMO2lLX63HGHSEb","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bq7IhHYhACLV5lOnC+pFCyqZLIgeWKvuia8LXsuFPFoR","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BrCdX5RVSC4e/tVnXswe/sAoZwMpViiMaye3nbtlBt1g","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BiXGglnhoL09drdLqes3xjunZmc7qvb62T9CQIflSkUn","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bvb90IZ2BFjqm+ohA4WfbGYDr9lPPfAsVq6NYTlJl77V","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BqeJnaVnEL+c6WmKSTMlzWsuIFoxt7Pw7EU5iKwxByM7","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bi1VSM37dP9+LvHsce22Gm+KBFjkoZNIv72zyNMbecmv","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BpN52GiW7ls6oyRqGBhlWapyrCsJ/dKpLz5DWBA+csnc","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bs546Gn3O+BgDuVOpQX5Yq52qzqpEnqCVL/hlN1gVza7","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BmiKugJsMSlzw59O7lFlAVoLq5kGVwULIOgsKrIAWx/z","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BjBdI01cxxAA/j0CaEZQONk/pPxoUZUM3duOttSPuUBQ"],"subtree_root_proofs":[{"start":42,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pobQGTlJ1DEe35QGR/uyCRadbeq8B/WFgc5loaquKjNu","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641prYmHDA06DMGJLLLXgxfv8npU/lQeA6HyVXV1UnnBsxL","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641puUqxintB9Cr09jxLLLDiY4SPF8RkWEmYvny7XQA3Q9n","/////////////////////////////////////////////////////////////////////////////xMiHGZgvmljQTwaTRq9CPPmjKgEv83Eqic8234bCbwF"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////0OH3EtYzMozLhtgI1sLi8PRzbTxq2SD5DqsaCL91xFk"],"is_max_namespace_ignored":true},{"end":35,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BluqSdnZgPfNDH/QkaQR6740Av54jOWQJ3GrO4DF7ig0","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYBa6EOLJBa0LR2iCv77iS96VQQIcaFhqAxCHdLVwTVHb","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYHvjivyc0j8QgCCszgoiDajSWrfgZC31dfc90vjctEFo","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYIzJMVPlQPQdJVKxustF8fKbKeRUVKEcu/CE+pLRVihY","/////////////////////////////////////////////////////////////////////////////zWOe5vJ0ndF1DmsdTXfBNmsBjnI2SK6LPKfX/HpAVgu"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bse3dSwJc8yhF5TGDq626xQGp1/xw0hq8a0mJp4gOLvl","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bjl92I8UYW5QjNuWT5+F6WDBRiS+OoJmA+7THAnr1jNe","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYCRvav532xTB9wHf5Gk/RBVkqmCYDeFl/1z5H0i5j5Lt"],"proofs":[{"total":256,"index":3,"leaf_hash":"mslppPgA7eyD6n3h8a0rTObSz4YDdCsy5qCFZ8VfeVo=","aunts":["90m0xA3qn7xoqiU3bYWMKokfMmxh+8t2RPXqKXlFGYY=","rO6aXvtUMKMnLuv8PKog8KG2YepGGDDKBvvb86e3dkY=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":4,"leaf_hash":"RmQYFxbaDZTrJGsRq0PwA7b4hPVT6iIb1KA8UQxLzXk=","aunts":["pCw4CCNMveZWlFDa11ODcfggZK/THf/ZmMHtCjbYu6g=","2MRQ3/Txb8R44h+MPYGILPasJAYvfavF3gWETOr5Vh8=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":5,"leaf_hash":"pCw4CCNMveZWlFDa11ODcfggZK/THf/ZmMHtCjbYu6g=","aunts":["RmQYFxbaDZTrJGsRq0PwA7b4hPVT6iIb1KA8UQxLzXk=","2MRQ3/Txb8R44h+MPYGILPasJAYvfavF3gWETOr5Vh8=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":3,"end_row":5},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":42,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641pobQGTlJ1DEe35QGR/uyCRadbeq8B/WFgc5loaquKjNu","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641prYmHDA06DMGJLLLXgxfv8npU/lQeA6HyVXV1UnnBsxL","AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAAKaafRnPSg641puUqxintB9Cr09jxLLLDiY4SPF8RkWEmYvny7XQA3Q9n","/////////////////////////////////////////////////////////////////////////////xMiHGZgvmljQTwaTRq9CPPmjKgEv83Eqic8234bCbwF"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////0OH3EtYzMozLhtgI1sLi8PRzbTxq2SD5DqsaCL91xFk"],"is_max_namespace_ignored":true},{"end":35,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53BluqSdnZgPfNDH/QkaQR6740Av54jOWQJ3GrO4DF7ig0","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYBa6EOLJBa0LR2iCv77iS96VQQIcaFhqAxCHdLVwTVHb","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYHvjivyc0j8QgCCszgoiDajSWrfgZC31dfc90vjctEFo","AAAAAAAAAAAAAAAAAAAAAAAAAES/GZDmkNURqmAAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYIzJMVPlQPQdJVKxustF8fKbKeRUVKEcu/CE+pLRVihY","/////////////////////////////////////////////////////////////////////////////zWOe5vJ0ndF1DmsdTXfBNmsBjnI2SK6LPKfX/HpAVgu"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACmmn0Zz0oOuNaYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bse3dSwJc8yhF5TGDq626xQGp1/xw0hq8a0mJp4gOLvl","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAANXaZQci+cm53Bjl92I8UYW5QjNuWT5+F6WDBRiS+OoJmA+7THAnr1jNe","AAAAAAAAAAAAAAAAAAAAAAAAADV2mUHIvnJudwYAAAAAAAAAAAAAAAAAAAAAAAAARL8ZkOaQ1RGqYCRvav532xTB9wHf5Gk/RBVkqmCYDeFl/1z5H0i5j5Lt"],"proofs":[{"total":256,"index":3,"leaf_hash":"mslppPgA7eyD6n3h8a0rTObSz4YDdCsy5qCFZ8VfeVo=","aunts":["90m0xA3qn7xoqiU3bYWMKokfMmxh+8t2RPXqKXlFGYY=","rO6aXvtUMKMnLuv8PKog8KG2YepGGDDKBvvb86e3dkY=","XRblFe2SQXNNvsCFBLof7AzBKfAUxTGP7yRbOKYtHBc=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":4,"leaf_hash":"RmQYFxbaDZTrJGsRq0PwA7b4hPVT6iIb1KA8UQxLzXk=","aunts":["pCw4CCNMveZWlFDa11ODcfggZK/THf/ZmMHtCjbYu6g=","2MRQ3/Txb8R44h+MPYGILPasJAYvfavF3gWETOr5Vh8=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":5,"leaf_hash":"pCw4CCNMveZWlFDa11ODcfggZK/THf/ZmMHtCjbYu6g=","aunts":["RmQYFxbaDZTrJGsRq0PwA7b4hPVT6iIb1KA8UQxLzXk=","2MRQ3/Txb8R44h+MPYGILPasJAYvfavF3gWETOr5Vh8=","Msj1cRM2vFeZiuJmd88uZ5G7PPTKxqNe46/AllMSHvY=","a2aakKmI8aMqrjPgNemoo8i3RDYvDxq9x6EM4lxhVpE=","6VfUHiAhCxzO5vFS89/O2fxXR6040XNUDyZRLwvvtds=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":3,"end_row":5},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":2,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl527HFOGNJt5E7dHVtZU7XlELE1YyyGNay4UoSGd3aEkt","/////////////////////////////////////////////////////////////////////////////wP33atuVCjRFfbMKQ81ItA2GMgJoaLD/qQyLaG/TM9h"],"is_max_namespace_ignored":true},{"end":61,"nodes":["//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","//////////////////////////////////////7//////////////////////////////////////lrD0qJ9dspxSO1Yl8NDioZfgOm8Yj63Y+BGDRHlKCRj","/////////////////////////////////////////////////////////////////////////////wb6k6qVOMPRmbwzkMmflq7PzauKlnZ13BlutBA2Qr4E"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYTt+Ee2FqqJqIs1ebK44c0IyYX7btGyymz4ZAfeNSGMb","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmH//////////////////////////////////////tIeDTR2tV1gLa3ZBI63mnVxRDn5l8KBYhN1rvCfuJ8Z"],"proofs":[{"total":256,"index":16,"leaf_hash":"uuaVU96dx286rQ8bIzW7GlXCqNW5DNy5ZOaWshe3yDw=","aunts":["4XoaH71nf3OK/TcGqG4+Lf9SwoG9SwAttrPCCeO/3uQ=","GCgBzrYrTrKvoB66cGffHYlFLQpYNGj2CPTng+xPLt0=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","CCX/QVcnImltJR7SKm+zI8Jd67C4pdqsK/7cq+YUtRU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":17,"leaf_hash":"4XoaH71nf3OK/TcGqG4+Lf9SwoG9SwAttrPCCeO/3uQ=","aunts":["uuaVU96dx286rQ8bIzW7GlXCqNW5DNy5ZOaWshe3yDw=","GCgBzrYrTrKvoB66cGffHYlFLQpYNGj2CPTng+xPLt0=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","CCX/QVcnImltJR7SKm+zI8Jd67C4pdqsK/7cq+YUtRU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":16,"end_row":17},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":2,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA5DA3Z6nLfKbl527HFOGNJt5E7dHVtZU7XlELE1YyyGNay4UoSGd3aEkt","/////////////////////////////////////////////////////////////////////////////wP33atuVCjRFfbMKQ81ItA2GMgJoaLD/qQyLaG/TM9h"],"is_max_namespace_ignored":true},{"end":61,"nodes":["//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","//////////////////////////////////////7//////////////////////////////////////lrD0qJ9dspxSO1Yl8NDioZfgOm8Yj63Y+BGDRHlKCRj","/////////////////////////////////////////////////////////////////////////////wb6k6qVOMPRmbwzkMmflq7PzauKlnZ13BlutBA2Qr4E"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOQwN2epy3ym5ecAAAAAAAAAAAAAAAAAAAAAAAAA8OuiH9JH71smYTt+Ee2FqqJqIs1ebK44c0IyYX7btGyymz4ZAfeNSGMb","AAAAAAAAAAAAAAAAAAAAAAAAAPDroh/SR+9bJmH//////////////////////////////////////tIeDTR2tV1gLa3ZBI63mnVxRDn5l8KBYhN1rvCfuJ8Z"],"proofs":[{"total":256,"index":16,"leaf_hash":"uuaVU96dx286rQ8bIzW7GlXCqNW5DNy5ZOaWshe3yDw=","aunts":["4XoaH71nf3OK/TcGqG4+Lf9SwoG9SwAttrPCCeO/3uQ=","GCgBzrYrTrKvoB66cGffHYlFLQpYNGj2CPTng+xPLt0=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","CCX/QVcnImltJR7SKm+zI8Jd67C4pdqsK/7cq+YUtRU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]},{"total":256,"index":17,"leaf_hash":"4XoaH71nf3OK/TcGqG4+Lf9SwoG9SwAttrPCCeO/3uQ=","aunts":["uuaVU96dx286rQ8bIzW7GlXCqNW5DNy5ZOaWshe3yDw=","GCgBzrYrTrKvoB66cGffHYlFLQpYNGj2CPTng+xPLt0=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","CCX/QVcnImltJR7SKm+zI8Jd67C4pdqsK/7cq+YUtRU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","cp0QNGeMHyDswPDAB3Sm4DEKtnnunC4Rky85mbQQsms=","1rfzrQpPMj/x/BlQ+bKZGcL2fo2PGLmkkxkZaIgGlsw="]}],"start_row":16,"end_row":17},"namespace_version":0},"root":"YmHAJXSK3QNEIc2JN+Pqi6eVKRurotBa1yNj+k0Ko8I=","sub_threshold":64}] \ No newline at end of file diff --git a/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e31353020736861726573.json b/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e31353020736861726573.json new file mode 100755 index 0000000000..a49690da88 --- /dev/null +++ b/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e31353020736861726573.json @@ -0,0 +1 @@ +[{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":44,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1oWHvUa1jVzL2r5AuHJD/sSPZFz2+Ccz1mLh18zPIp1L","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1pyDfl3PAmzIOuWq3rg+kA0QhTdJVpXKTvkLfZZBh725","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1uOHdmQFzw/waOhtffpnzKW7dBq4BgggzI+wPQ2bLe/o","/////////////////////////////////////////////////////////////////////////////yFrmWa2DAMyTBxLsmEDgumNCyTJo2fSVvKYvZxGX1TO"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2dWELEW9Uj5nbD2b+hNrzkypb6L3/3FxFAz/IS8Usa9"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+16MJyrfCowU0ZxSZHsOCBuLBb9WFW7XGqTebbN8X9g"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTi7WnX4m5M3NVierjKMnaoiWlp69ckCQlqYM5Ls3iWrx","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTpbKshqTOf9BJnzNQ1BxBTfE8MxBC4RQZNah3qfihPf9","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTl8ErL//3L+ydmC+slpb0r1ifEFbz3bDKTQMVKXNaR+U","/////////////////////////////////////////////////////////////////////////////+YDOSsgbYakhxElByu+f4LBi4sxv9NyNjvKWivVomg2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHCJxvGpPG2LYHDIJF0YjdzwRLsf2Bfwg0+utB0F9Ngl7","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHNsORiJc1YKJ+7OzsTVjGT31II3rowPD+ceva2aWvczN","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHPn3cV08LnoCiv6dcMq3MGjvuGv7hBdBBju9hYG51Co7","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTh4tJk+XUmlwXrxh/RHKhvSs8d1jzQJkzpnoIu9ZqZ2m"],"proofs":[{"total":256,"index":10,"leaf_hash":"T07KAzShLgjmFTBMf4uylQDrj4a4AjaQFQ4j0FKdNNw=","aunts":["VHGWzKblonrogxImyeRCFhuHi4jTxKjInRhJ342LmL4=","OCFVudzqJXNw0lD6gwqeQJddAfDFL/hdlvE1zoBkRf0=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":11,"leaf_hash":"VHGWzKblonrogxImyeRCFhuHi4jTxKjInRhJ342LmL4=","aunts":["T07KAzShLgjmFTBMf4uylQDrj4a4AjaQFQ4j0FKdNNw=","OCFVudzqJXNw0lD6gwqeQJddAfDFL/hdlvE1zoBkRf0=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":12,"leaf_hash":"LNfisMalXhDO1eT0X9IyTcCyVMSPNmvueTP+PlHaYVk=","aunts":["dm1JDw6QukYMx51uePAijahE6WsYNJkkpHE2ukYyCUY=","cDE3yILc8k7xLhOATBRsISL6znLBSmAT+6lFio1Xgiw=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":13,"leaf_hash":"dm1JDw6QukYMx51uePAijahE6WsYNJkkpHE2ukYyCUY=","aunts":["LNfisMalXhDO1eT0X9IyTcCyVMSPNmvueTP+PlHaYVk=","cDE3yILc8k7xLhOATBRsISL6znLBSmAT+6lFio1Xgiw=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":10,"end_row":13},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":44,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1oWHvUa1jVzL2r5AuHJD/sSPZFz2+Ccz1mLh18zPIp1L","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1pyDfl3PAmzIOuWq3rg+kA0QhTdJVpXKTvkLfZZBh725","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1uOHdmQFzw/waOhtffpnzKW7dBq4BgggzI+wPQ2bLe/o","/////////////////////////////////////////////////////////////////////////////yFrmWa2DAMyTBxLsmEDgumNCyTJo2fSVvKYvZxGX1TO"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2dWELEW9Uj5nbD2b+hNrzkypb6L3/3FxFAz/IS8Usa9"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+16MJyrfCowU0ZxSZHsOCBuLBb9WFW7XGqTebbN8X9g"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTi7WnX4m5M3NVierjKMnaoiWlp69ckCQlqYM5Ls3iWrx","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTpbKshqTOf9BJnzNQ1BxBTfE8MxBC4RQZNah3qfihPf9","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTl8ErL//3L+ydmC+slpb0r1ifEFbz3bDKTQMVKXNaR+U","/////////////////////////////////////////////////////////////////////////////+YDOSsgbYakhxElByu+f4LBi4sxv9NyNjvKWivVomg2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHCJxvGpPG2LYHDIJF0YjdzwRLsf2Bfwg0+utB0F9Ngl7","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHNsORiJc1YKJ+7OzsTVjGT31II3rowPD+ceva2aWvczN","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHPn3cV08LnoCiv6dcMq3MGjvuGv7hBdBBju9hYG51Co7","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTh4tJk+XUmlwXrxh/RHKhvSs8d1jzQJkzpnoIu9ZqZ2m"],"proofs":[{"total":256,"index":10,"leaf_hash":"T07KAzShLgjmFTBMf4uylQDrj4a4AjaQFQ4j0FKdNNw=","aunts":["VHGWzKblonrogxImyeRCFhuHi4jTxKjInRhJ342LmL4=","OCFVudzqJXNw0lD6gwqeQJddAfDFL/hdlvE1zoBkRf0=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":11,"leaf_hash":"VHGWzKblonrogxImyeRCFhuHi4jTxKjInRhJ342LmL4=","aunts":["T07KAzShLgjmFTBMf4uylQDrj4a4AjaQFQ4j0FKdNNw=","OCFVudzqJXNw0lD6gwqeQJddAfDFL/hdlvE1zoBkRf0=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":12,"leaf_hash":"LNfisMalXhDO1eT0X9IyTcCyVMSPNmvueTP+PlHaYVk=","aunts":["dm1JDw6QukYMx51uePAijahE6WsYNJkkpHE2ukYyCUY=","cDE3yILc8k7xLhOATBRsISL6znLBSmAT+6lFio1Xgiw=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":13,"leaf_hash":"dm1JDw6QukYMx51uePAijahE6WsYNJkkpHE2ukYyCUY=","aunts":["LNfisMalXhDO1eT0X9IyTcCyVMSPNmvueTP+PlHaYVk=","cDE3yILc8k7xLhOATBRsISL6znLBSmAT+6lFio1Xgiw=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":10,"end_row":13},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9JBhiPV08K9yzhTxmSQeANzTJHoNMooPb1jENVK2NGI","/////////////////////////////////////////////////////////////////////////////wJuv65VHijNwndRLg+/JhkZIXxAbUYHeRZTvPyzuMuD"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////0Nk6modtu8Svh/9ZdZtrxerXT4hBWxwzaLpHt3b+y/J"],"is_max_namespace_ignored":true},{"end":38,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CV/Qq9U01iHkRR9cf+z2PQk0pKFpWrv9Dux32wJdcju2","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRfH7MUJ/pD7WpXczUa+OTfR2eaNzBK0+J/46w65R07y/","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRd0MAxXGFhUZylLOgxdBcQDbIeBaUGkswO44zERtRN4a","/////////////////////////////////////////////////////////////////////////////38AyE03Tpt8B8FGFlSIqPobjkTPgRkjzizlMpyU67Jd"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7Cbk4UuAW2xcmWbcTf9f3/WYu9mqE+iTTjlC0aT43WdU9","AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CUBb49EuEP43TKgPW9Rbtw+pYc96dJJxzcrEhEaH/L1F","AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRe9kornrcOFqpDVWU6wh1WAnhudf62j1UmP1G6GgXZbG"],"proofs":[{"total":256,"leaf_hash":"fw+/kFltdGXzLDvL10UnIenKnxMfedGd3XlARZ3pRew=","aunts":["Tp5/UfAn7dyWGIobOuHRon2R/pG+eYJcEqLRjtvE31I=","Rv1+yFc/0meW/yYVzSl6d7VSVzaWXtWsIGzw0Cjv/zs=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":1,"leaf_hash":"Tp5/UfAn7dyWGIobOuHRon2R/pG+eYJcEqLRjtvE31I=","aunts":["fw+/kFltdGXzLDvL10UnIenKnxMfedGd3XlARZ3pRew=","Rv1+yFc/0meW/yYVzSl6d7VSVzaWXtWsIGzw0Cjv/zs=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":2,"leaf_hash":"Mk/gSoEKQT0ZPL3KIer0UkpRdtkbx56XHiqqMaGVtrk=","aunts":["LYgYw+KhCIZcEkKSTff8Tv1ecvgBNAjaFNVjEHI/1V4=","2ZMrGbBti6vOpyA5+GwAiPG/WUrD8G0R+UadnuOgGnE=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"end_row":2},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9JBhiPV08K9yzhTxmSQeANzTJHoNMooPb1jENVK2NGI","/////////////////////////////////////////////////////////////////////////////wJuv65VHijNwndRLg+/JhkZIXxAbUYHeRZTvPyzuMuD"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////0Nk6modtu8Svh/9ZdZtrxerXT4hBWxwzaLpHt3b+y/J"],"is_max_namespace_ignored":true},{"end":38,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CV/Qq9U01iHkRR9cf+z2PQk0pKFpWrv9Dux32wJdcju2","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRfH7MUJ/pD7WpXczUa+OTfR2eaNzBK0+J/46w65R07y/","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRd0MAxXGFhUZylLOgxdBcQDbIeBaUGkswO44zERtRN4a","/////////////////////////////////////////////////////////////////////////////38AyE03Tpt8B8FGFlSIqPobjkTPgRkjzizlMpyU67Jd"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7Cbk4UuAW2xcmWbcTf9f3/WYu9mqE+iTTjlC0aT43WdU9","AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CUBb49EuEP43TKgPW9Rbtw+pYc96dJJxzcrEhEaH/L1F","AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRe9kornrcOFqpDVWU6wh1WAnhudf62j1UmP1G6GgXZbG"],"proofs":[{"total":256,"leaf_hash":"fw+/kFltdGXzLDvL10UnIenKnxMfedGd3XlARZ3pRew=","aunts":["Tp5/UfAn7dyWGIobOuHRon2R/pG+eYJcEqLRjtvE31I=","Rv1+yFc/0meW/yYVzSl6d7VSVzaWXtWsIGzw0Cjv/zs=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":1,"leaf_hash":"Tp5/UfAn7dyWGIobOuHRon2R/pG+eYJcEqLRjtvE31I=","aunts":["fw+/kFltdGXzLDvL10UnIenKnxMfedGd3XlARZ3pRew=","Rv1+yFc/0meW/yYVzSl6d7VSVzaWXtWsIGzw0Cjv/zs=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":2,"leaf_hash":"Mk/gSoEKQT0ZPL3KIer0UkpRdtkbx56XHiqqMaGVtrk=","aunts":["LYgYw+KhCIZcEkKSTff8Tv1ecvgBNAjaFNVjEHI/1V4=","2ZMrGbBti6vOpyA5+GwAiPG/WUrD8G0R+UadnuOgGnE=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"end_row":2},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":44,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnahyL/E6mc1udVwmznAgCahpOd7fgaOt8yo+PquYqb/YT","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anaqf0PE0LayG/Vh3zmQOTXTNHcEr/VS/uMbR1fxNxFRG8","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Analvu+m0DAjbX6vvtuLCcLiceUcmXj25UE2xDUpBVK+Rq","/////////////////////////////////////////////////////////////////////////////xCEL5rjIalY0Uz0KMkhMmatTkE0bdypzkWlQEDonE0c"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////36csm9IqGjb9+GVQGm9s938bo9UTc1LPhMBfrJVXIj5"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////1iswXz0m2N0iOZXLcpgA9MTTmNmKJzS4JOtj/N3agyi"],"is_max_namespace_ignored":true},{"end":12,"nodes":["//////////////////////////////////////7//////////////////////////////////////pNkZFSQk3XAFlq5+qVKnFnJIHi/zkuKy8dZMp9iFzwd","//////////////////////////////////////7//////////////////////////////////////i78l7x6hc+r4PDOj/ojJFtChc0fWOiDF42n8pbp/dUO","//////////////////////////////////////7//////////////////////////////////////nv6BM04fuwZggth+O9G6qCRn1vSz9cURJ32UN+p8l7F","/////////////////////////////////////////////////////////////////////////////9VdLgmbMM1/aZwP0HPQA7uMX6eC9ZLE1y/iYVHt2E4c"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2Fi4FN5HMkHbswBS+ezJvNTxBnde87+8+VEDZwHkkpk5","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2D7VDkQBLdoGoK0M0eq4QJjGHCht0XAUqTx4xIVMqL82","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2CH7ubx0VJH2bX7ISmPqOXnll7ZesgFOJrag+lSSkAIE","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNj//////////////////////////////////////k9LlvQR9EqvdTZYC2/MAT/WpUaDl3fLqzmhjqptFlSK"],"proofs":[{"total":256,"index":23,"leaf_hash":"XwOnYkJHph0FFv2iYk+JYN0Fz3UzKnOEhzYR20BPFOs=","aunts":["jZNkvkRKXMy8UwLO90eZRkd9p2NxrVvE4GGk3qTXm9Y=","aKBVBrxZ5yQgWxVqXOm/7dYZwxeTbDKtrFxY6+A25qk=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":24,"leaf_hash":"0K4GnbELWzN+85bJG83R7fdawC/T/sG7GE8DKOMN6m4=","aunts":["bKzE7CcudCXDssCxF0chvL5Vxsv7QKyE/431hONPDC0=","ez2/SkWuJ2X5YdY+HjNsMMY4NTpVFvhB/uG7Tlsf6Sw=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","hOZR9lZbjCQ2M2k1FsYvua3b87HT4cFf+Fc0e5XUc6E=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":25,"leaf_hash":"bKzE7CcudCXDssCxF0chvL5Vxsv7QKyE/431hONPDC0=","aunts":["0K4GnbELWzN+85bJG83R7fdawC/T/sG7GE8DKOMN6m4=","ez2/SkWuJ2X5YdY+HjNsMMY4NTpVFvhB/uG7Tlsf6Sw=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","hOZR9lZbjCQ2M2k1FsYvua3b87HT4cFf+Fc0e5XUc6E=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":26,"leaf_hash":"ACbgbB/WyyF/RodyonAQGp39kKwuyqaFhI2DkZDgDHA=","aunts":["d9fGI1EsGrgEWpF/mcULqgXcZV9XCv2G1rpFOlZVzYc=","BlinCBjTT2sLpxEnuNs0COaisDsbluh0q/twncz7xkI=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","hOZR9lZbjCQ2M2k1FsYvua3b87HT4cFf+Fc0e5XUc6E=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":23,"end_row":26},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":44,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnahyL/E6mc1udVwmznAgCahpOd7fgaOt8yo+PquYqb/YT","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anaqf0PE0LayG/Vh3zmQOTXTNHcEr/VS/uMbR1fxNxFRG8","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Analvu+m0DAjbX6vvtuLCcLiceUcmXj25UE2xDUpBVK+Rq","/////////////////////////////////////////////////////////////////////////////xCEL5rjIalY0Uz0KMkhMmatTkE0bdypzkWlQEDonE0c"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////36csm9IqGjb9+GVQGm9s938bo9UTc1LPhMBfrJVXIj5"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////1iswXz0m2N0iOZXLcpgA9MTTmNmKJzS4JOtj/N3agyi"],"is_max_namespace_ignored":true},{"end":12,"nodes":["//////////////////////////////////////7//////////////////////////////////////pNkZFSQk3XAFlq5+qVKnFnJIHi/zkuKy8dZMp9iFzwd","//////////////////////////////////////7//////////////////////////////////////i78l7x6hc+r4PDOj/ojJFtChc0fWOiDF42n8pbp/dUO","//////////////////////////////////////7//////////////////////////////////////nv6BM04fuwZggth+O9G6qCRn1vSz9cURJ32UN+p8l7F","/////////////////////////////////////////////////////////////////////////////9VdLgmbMM1/aZwP0HPQA7uMX6eC9ZLE1y/iYVHt2E4c"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2Fi4FN5HMkHbswBS+ezJvNTxBnde87+8+VEDZwHkkpk5","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2D7VDkQBLdoGoK0M0eq4QJjGHCht0XAUqTx4xIVMqL82","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2CH7ubx0VJH2bX7ISmPqOXnll7ZesgFOJrag+lSSkAIE","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNj//////////////////////////////////////k9LlvQR9EqvdTZYC2/MAT/WpUaDl3fLqzmhjqptFlSK"],"proofs":[{"total":256,"index":23,"leaf_hash":"XwOnYkJHph0FFv2iYk+JYN0Fz3UzKnOEhzYR20BPFOs=","aunts":["jZNkvkRKXMy8UwLO90eZRkd9p2NxrVvE4GGk3qTXm9Y=","aKBVBrxZ5yQgWxVqXOm/7dYZwxeTbDKtrFxY6+A25qk=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":24,"leaf_hash":"0K4GnbELWzN+85bJG83R7fdawC/T/sG7GE8DKOMN6m4=","aunts":["bKzE7CcudCXDssCxF0chvL5Vxsv7QKyE/431hONPDC0=","ez2/SkWuJ2X5YdY+HjNsMMY4NTpVFvhB/uG7Tlsf6Sw=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","hOZR9lZbjCQ2M2k1FsYvua3b87HT4cFf+Fc0e5XUc6E=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":25,"leaf_hash":"bKzE7CcudCXDssCxF0chvL5Vxsv7QKyE/431hONPDC0=","aunts":["0K4GnbELWzN+85bJG83R7fdawC/T/sG7GE8DKOMN6m4=","ez2/SkWuJ2X5YdY+HjNsMMY4NTpVFvhB/uG7Tlsf6Sw=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","hOZR9lZbjCQ2M2k1FsYvua3b87HT4cFf+Fc0e5XUc6E=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":26,"leaf_hash":"ACbgbB/WyyF/RodyonAQGp39kKwuyqaFhI2DkZDgDHA=","aunts":["d9fGI1EsGrgEWpF/mcULqgXcZV9XCv2G1rpFOlZVzYc=","BlinCBjTT2sLpxEnuNs0COaisDsbluh0q/twncz7xkI=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","hOZR9lZbjCQ2M2k1FsYvua3b87HT4cFf+Fc0e5XUc6E=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":23,"end_row":26},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHLF+LcSxhe2poWiE+7iI4udOkcEb6S9nb/l/6eQxH/x3","/////////////////////////////////////////////////////////////////////////////+YDOSsgbYakhxElByu+f4LBi4sxv9NyNjvKWivVomg2"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yZ1UMgxbn0uv30QYVo6eAeGOnzyq5YG4GN/2v7/a31B"],"is_max_namespace_ignored":true},{"end":42,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTmEYxMgyjvwvMNjmtEtp4CBycQoTeKEcL8S36YiWtv/1","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGLF2dZrQ2Rn4xzZy5EnbdKg95F7f93TQCFQLE1g28I4Y","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGH5Lw2lw/56Ph9a81abpMOuv74+SDOpVGgGCeBogLKVn","/////////////////////////////////////////////////////////////////////////////88LEzpo3WHpvi5ozAu6b7v3v9VTUg6/ML55zUu6wMs7"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTh4tJk+XUmlwXrxh/RHKhvSs8d1jzQJkzpnoIu9ZqZ2m","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTiFhwdgl3LLbCu/DWKhuaW0/MuKTwUqByyq6K9HFgVoT","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGOGGOP4lAb4Zwiu1IkuM2jIgLRLNbUY9LF/ksJIDFVZd"],"proofs":[{"total":256,"index":13,"leaf_hash":"dm1JDw6QukYMx51uePAijahE6WsYNJkkpHE2ukYyCUY=","aunts":["LNfisMalXhDO1eT0X9IyTcCyVMSPNmvueTP+PlHaYVk=","cDE3yILc8k7xLhOATBRsISL6znLBSmAT+6lFio1Xgiw=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":14,"leaf_hash":"vjMFXUrjb9E/HnSDsHlqXchokirc0+ODJxvjIGNX63k=","aunts":["f9i2babDNafVo8XCm+6mglKjM0yvSQYzCGWVpRrZDG4=","GVEHSNKTEGN4LlmzUPUfoRho+V2LbXBfqkLTWWqAYUU=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":15,"leaf_hash":"f9i2babDNafVo8XCm+6mglKjM0yvSQYzCGWVpRrZDG4=","aunts":["vjMFXUrjb9E/HnSDsHlqXchokirc0+ODJxvjIGNX63k=","GVEHSNKTEGN4LlmzUPUfoRho+V2LbXBfqkLTWWqAYUU=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":13,"end_row":15},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHLF+LcSxhe2poWiE+7iI4udOkcEb6S9nb/l/6eQxH/x3","/////////////////////////////////////////////////////////////////////////////+YDOSsgbYakhxElByu+f4LBi4sxv9NyNjvKWivVomg2"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yZ1UMgxbn0uv30QYVo6eAeGOnzyq5YG4GN/2v7/a31B"],"is_max_namespace_ignored":true},{"end":42,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTmEYxMgyjvwvMNjmtEtp4CBycQoTeKEcL8S36YiWtv/1","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGLF2dZrQ2Rn4xzZy5EnbdKg95F7f93TQCFQLE1g28I4Y","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGH5Lw2lw/56Ph9a81abpMOuv74+SDOpVGgGCeBogLKVn","/////////////////////////////////////////////////////////////////////////////88LEzpo3WHpvi5ozAu6b7v3v9VTUg6/ML55zUu6wMs7"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTh4tJk+XUmlwXrxh/RHKhvSs8d1jzQJkzpnoIu9ZqZ2m","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTiFhwdgl3LLbCu/DWKhuaW0/MuKTwUqByyq6K9HFgVoT","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGOGGOP4lAb4Zwiu1IkuM2jIgLRLNbUY9LF/ksJIDFVZd"],"proofs":[{"total":256,"index":13,"leaf_hash":"dm1JDw6QukYMx51uePAijahE6WsYNJkkpHE2ukYyCUY=","aunts":["LNfisMalXhDO1eT0X9IyTcCyVMSPNmvueTP+PlHaYVk=","cDE3yILc8k7xLhOATBRsISL6znLBSmAT+6lFio1Xgiw=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":14,"leaf_hash":"vjMFXUrjb9E/HnSDsHlqXchokirc0+ODJxvjIGNX63k=","aunts":["f9i2babDNafVo8XCm+6mglKjM0yvSQYzCGWVpRrZDG4=","GVEHSNKTEGN4LlmzUPUfoRho+V2LbXBfqkLTWWqAYUU=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":15,"leaf_hash":"f9i2babDNafVo8XCm+6mglKjM0yvSQYzCGWVpRrZDG4=","aunts":["vjMFXUrjb9E/HnSDsHlqXchokirc0+ODJxvjIGNX63k=","GVEHSNKTEGN4LlmzUPUfoRho+V2LbXBfqkLTWWqAYUU=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":13,"end_row":15},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":24,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGJs5xHVe1lSRFjYcPdA+QBb3PVfAKHAVqDR6W8Dm5pNc","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGOb2/DkIW7Ad40NuuAyDsWrXepwCDUQ9zRy+YnDosIk7","/////////////////////////////////////////////////////////////////////////////7WLsIvpJ52ieh1Hj2lSe/FsmtQ+F9gDlbg20HUx+qnr"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3SUWeOMQqKg/j4n/uZd6Fes40g/AJc9Qh017132VHBM"],"is_max_namespace_ignored":true},{"end":60,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnarjJr6fjL0nPygcR27MdyOezXyjSAw0jTZlPjJ6N3IMC","/////////////////////////////////////////////////////////////////////////////+dP/D4MdoGa22KND6HRKC6EjIv/m+Ui3D8dKmRHjBaK"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKAwSNVok0TprlWx9njOFwbtrODIHvUUWEoTUacSpe6JW","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKF81+YmemVGjYlEXBPjrfvcofaWbKfoi26tCQuiJLN3a","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anas8fQAYzdyUEO0t8ktNxv+4FrYxQSfeOn/lbX/1m0Rm7"],"proofs":[{"total":256,"index":18,"leaf_hash":"IMGvE6gDhQ0wvbqEgLYbgCJc5DYiR0pDsSqFN8jI/nk=","aunts":["OY+4AvwKfn06lSYX67YsPpO3IleRyjiFeruuk2k6e5I=","WcdSCE3kkEE4eHW8LlKEiTOyJSQ7yChfRu7TQuUfXz8=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":19,"leaf_hash":"OY+4AvwKfn06lSYX67YsPpO3IleRyjiFeruuk2k6e5I=","aunts":["IMGvE6gDhQ0wvbqEgLYbgCJc5DYiR0pDsSqFN8jI/nk=","WcdSCE3kkEE4eHW8LlKEiTOyJSQ7yChfRu7TQuUfXz8=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":20,"leaf_hash":"OBkuDEskGWb4cVfyORX7wEomJLL9NrXoaQZGsqNXauE=","aunts":["+GoVs3pwWV+2h5HDt8I1Bg9Lag9DZIuK1Rv0P+IUC+M=","FLrz0bqhHW+G5DvkfcZIZVSu9WnbPDET9hVSIUnIOYM=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":18,"end_row":20},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":24,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGJs5xHVe1lSRFjYcPdA+QBb3PVfAKHAVqDR6W8Dm5pNc","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGOb2/DkIW7Ad40NuuAyDsWrXepwCDUQ9zRy+YnDosIk7","/////////////////////////////////////////////////////////////////////////////7WLsIvpJ52ieh1Hj2lSe/FsmtQ+F9gDlbg20HUx+qnr"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3SUWeOMQqKg/j4n/uZd6Fes40g/AJc9Qh017132VHBM"],"is_max_namespace_ignored":true},{"end":60,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnarjJr6fjL0nPygcR27MdyOezXyjSAw0jTZlPjJ6N3IMC","/////////////////////////////////////////////////////////////////////////////+dP/D4MdoGa22KND6HRKC6EjIv/m+Ui3D8dKmRHjBaK"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKAwSNVok0TprlWx9njOFwbtrODIHvUUWEoTUacSpe6JW","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKF81+YmemVGjYlEXBPjrfvcofaWbKfoi26tCQuiJLN3a","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anas8fQAYzdyUEO0t8ktNxv+4FrYxQSfeOn/lbX/1m0Rm7"],"proofs":[{"total":256,"index":18,"leaf_hash":"IMGvE6gDhQ0wvbqEgLYbgCJc5DYiR0pDsSqFN8jI/nk=","aunts":["OY+4AvwKfn06lSYX67YsPpO3IleRyjiFeruuk2k6e5I=","WcdSCE3kkEE4eHW8LlKEiTOyJSQ7yChfRu7TQuUfXz8=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":19,"leaf_hash":"OY+4AvwKfn06lSYX67YsPpO3IleRyjiFeruuk2k6e5I=","aunts":["IMGvE6gDhQ0wvbqEgLYbgCJc5DYiR0pDsSqFN8jI/nk=","WcdSCE3kkEE4eHW8LlKEiTOyJSQ7yChfRu7TQuUfXz8=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":20,"leaf_hash":"OBkuDEskGWb4cVfyORX7wEomJLL9NrXoaQZGsqNXauE=","aunts":["+GoVs3pwWV+2h5HDt8I1Bg9Lag9DZIuK1Rv0P+IUC+M=","FLrz0bqhHW+G5DvkfcZIZVSu9WnbPDET9hVSIUnIOYM=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":18,"end_row":20},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":4,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETGMTKMHhpjPpWTsnqCrlsnXMRinWwg3V/ndVmQXQmHY","/////////////////////////////////////////////////////////////////////////////xRCwJvU7MDpz4gfeJO25ZtnbVbzBwp00q1FmFfVWBlk"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////9lBtNxMD6oybuQO89Czg7ktR8m4oH07dViZ2wwCegb5"],"is_max_namespace_ignored":true},{"end":42,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1mUWKWwn5nu8SGNBtZ6luevg+LdtMItZ4YM8sBYyeMUH","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHL0guHR7iWjxfuic4Dm2Nw/H5ot+SdMbWmTZxXiKXV+w","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHBFRw6QI6ufU8bq5qv/dABPmPy3CGbnmizFbYcy/6Q+f","/////////////////////////////////////////////////////////////////////////////yFrmWa2DAMyTBxLsmEDgumNCyTJo2fSVvKYvZxGX1TO"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1nrlx1hsCDLV8aEJ+kyszqC6I6AYgPlEBpr/b+OA+NDY","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1vTrKyLTQFF7kcpTRiVltPZrgPZvsKdhoRlnEILNc/g+","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHCJxvGpPG2LYHDIJF0YjdzwRLsf2Bfwg0+utB0F9Ngl7"],"proofs":[{"total":256,"index":8,"leaf_hash":"JH/ZifCts+YocCKYYHB9ohBDUved4FPhirkeY1ATH98=","aunts":["FbG0JjQmdVODTayyMWtxDXcZs+t1WtqXpm72K7wtbQA=","yO9x0X1Y5A+gYR0Xnfqsuajn5teS9yQkoHxzXIIBq6c=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":9,"leaf_hash":"FbG0JjQmdVODTayyMWtxDXcZs+t1WtqXpm72K7wtbQA=","aunts":["JH/ZifCts+YocCKYYHB9ohBDUved4FPhirkeY1ATH98=","yO9x0X1Y5A+gYR0Xnfqsuajn5teS9yQkoHxzXIIBq6c=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":10,"leaf_hash":"T07KAzShLgjmFTBMf4uylQDrj4a4AjaQFQ4j0FKdNNw=","aunts":["VHGWzKblonrogxImyeRCFhuHi4jTxKjInRhJ342LmL4=","OCFVudzqJXNw0lD6gwqeQJddAfDFL/hdlvE1zoBkRf0=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":8,"end_row":10},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":4,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETGMTKMHhpjPpWTsnqCrlsnXMRinWwg3V/ndVmQXQmHY","/////////////////////////////////////////////////////////////////////////////xRCwJvU7MDpz4gfeJO25ZtnbVbzBwp00q1FmFfVWBlk"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////9lBtNxMD6oybuQO89Czg7ktR8m4oH07dViZ2wwCegb5"],"is_max_namespace_ignored":true},{"end":42,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1mUWKWwn5nu8SGNBtZ6luevg+LdtMItZ4YM8sBYyeMUH","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHL0guHR7iWjxfuic4Dm2Nw/H5ot+SdMbWmTZxXiKXV+w","AAAAAAAAAAAAAAAAAAAAAAAAAE4R+1MPk8macxwAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHBFRw6QI6ufU8bq5qv/dABPmPy3CGbnmizFbYcy/6Q+f","/////////////////////////////////////////////////////////////////////////////yFrmWa2DAMyTBxLsmEDgumNCyTJo2fSVvKYvZxGX1TO"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1nrlx1hsCDLV8aEJ+kyszqC6I6AYgPlEBpr/b+OA+NDY","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1vTrKyLTQFF7kcpTRiVltPZrgPZvsKdhoRlnEILNc/g+","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAAThH7Uw+TyZpzHCJxvGpPG2LYHDIJF0YjdzwRLsf2Bfwg0+utB0F9Ngl7"],"proofs":[{"total":256,"index":8,"leaf_hash":"JH/ZifCts+YocCKYYHB9ohBDUved4FPhirkeY1ATH98=","aunts":["FbG0JjQmdVODTayyMWtxDXcZs+t1WtqXpm72K7wtbQA=","yO9x0X1Y5A+gYR0Xnfqsuajn5teS9yQkoHxzXIIBq6c=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":9,"leaf_hash":"FbG0JjQmdVODTayyMWtxDXcZs+t1WtqXpm72K7wtbQA=","aunts":["JH/ZifCts+YocCKYYHB9ohBDUved4FPhirkeY1ATH98=","yO9x0X1Y5A+gYR0Xnfqsuajn5teS9yQkoHxzXIIBq6c=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":10,"leaf_hash":"T07KAzShLgjmFTBMf4uylQDrj4a4AjaQFQ4j0FKdNNw=","aunts":["VHGWzKblonrogxImyeRCFhuHi4jTxKjInRhJ342LmL4=","OCFVudzqJXNw0lD6gwqeQJddAfDFL/hdlvE1zoBkRf0=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":8,"end_row":10},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CYgIMV1jYDshPHKPdLax0V/vJX1y5YoRqat/YkEUSPhK","AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CbvD2cQBwIL5g8hLSpA08Y08saZwEU3JuDfQ8wmcEoxL","/////////////////////////////////////////////////////////////////////////////38AyE03Tpt8B8FGFlSIqPobjkTPgRkjzizlMpyU67Jd"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+kYXg3KHqRnibIB3fjiBjBqgUbS3LOSLs2ILey0J/zo"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////4aAeilh9N1R4H18ZjGsjb+h3T9Ww6JGBIPL6MNepo8v"],"is_max_namespace_ignored":true},{"end":17,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRQPfzvl+TjA3GLfZ9X8FzaeE4jLFdXIAJTP6/MWgSi/I","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgReZT6Pumr3dugpBoSJu7esa2PDQ6NqzhjOEFujwKQE2/","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETP0Tx9WK7sAhwDitVsXIHXrwla3XwNLVj9CtQZdxcyx","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEd8amygKWP56hEo888vnX0aTXNl4wctqqYy5rphP2nUD","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEeHT8cofdku0aDde6JZS0EgsF+OOQoivocB8OPu22suy","/////////////////////////////////////////////////////////////////////////////5n6lv/xoiSafLA6LRjA5zklvw4nJbNZfFFUMxcuJkYg"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRe9kornrcOFqpDVWU6wh1WAnhudf62j1UmP1G6GgXZbG","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRQ6CJoIN2jiI8j5kOkO1ntzJZ+MHQuvI89JnZeyuAv1w","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRUmH9rm6aZ44IGdARF+q1mStUSCtlDBd7Y+3aNoSmGi3","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETz77NxuFk3duyefcPrC0SdDUlJ2+B41P9c7beT4hLl3"],"proofs":[{"total":256,"index":2,"leaf_hash":"Mk/gSoEKQT0ZPL3KIer0UkpRdtkbx56XHiqqMaGVtrk=","aunts":["LYgYw+KhCIZcEkKSTff8Tv1ecvgBNAjaFNVjEHI/1V4=","2ZMrGbBti6vOpyA5+GwAiPG/WUrD8G0R+UadnuOgGnE=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":3,"leaf_hash":"LYgYw+KhCIZcEkKSTff8Tv1ecvgBNAjaFNVjEHI/1V4=","aunts":["Mk/gSoEKQT0ZPL3KIer0UkpRdtkbx56XHiqqMaGVtrk=","2ZMrGbBti6vOpyA5+GwAiPG/WUrD8G0R+UadnuOgGnE=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":4,"leaf_hash":"4tNMeqJI8hfcuqx4dyA+tslLe2+dqAIWql00DpyZKvA=","aunts":["GHOXHtloA0zAOgtirG7yO5dH9D0V+/dZOxjeYgF7q7Q=","f5uyCPRE2n5uwOiLK8Ttkla2LS0AMne5wAb8WOE2tec=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":5,"leaf_hash":"GHOXHtloA0zAOgtirG7yO5dH9D0V+/dZOxjeYgF7q7Q=","aunts":["4tNMeqJI8hfcuqx4dyA+tslLe2+dqAIWql00DpyZKvA=","f5uyCPRE2n5uwOiLK8Ttkla2LS0AMne5wAb8WOE2tec=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":2,"end_row":5},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CYgIMV1jYDshPHKPdLax0V/vJX1y5YoRqat/YkEUSPhK","AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAK6feqTtCf8+7CbvD2cQBwIL5g8hLSpA08Y08saZwEU3JuDfQ8wmcEoxL","/////////////////////////////////////////////////////////////////////////////38AyE03Tpt8B8FGFlSIqPobjkTPgRkjzizlMpyU67Jd"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+kYXg3KHqRnibIB3fjiBjBqgUbS3LOSLs2ILey0J/zo"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////4aAeilh9N1R4H18ZjGsjb+h3T9Ww6JGBIPL6MNepo8v"],"is_max_namespace_ignored":true},{"end":17,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRQPfzvl+TjA3GLfZ9X8FzaeE4jLFdXIAJTP6/MWgSi/I","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgReZT6Pumr3dugpBoSJu7esa2PDQ6NqzhjOEFujwKQE2/","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETP0Tx9WK7sAhwDitVsXIHXrwla3XwNLVj9CtQZdxcyx","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEd8amygKWP56hEo888vnX0aTXNl4wctqqYy5rphP2nUD","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEeHT8cofdku0aDde6JZS0EgsF+OOQoivocB8OPu22suy","/////////////////////////////////////////////////////////////////////////////5n6lv/xoiSafLA6LRjA5zklvw4nJbNZfFFUMxcuJkYg"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACun3qk7Qn/PuwkAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRe9kornrcOFqpDVWU6wh1WAnhudf62j1UmP1G6GgXZbG","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRQ6CJoIN2jiI8j5kOkO1ntzJZ+MHQuvI89JnZeyuAv1w","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRUmH9rm6aZ44IGdARF+q1mStUSCtlDBd7Y+3aNoSmGi3","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETz77NxuFk3duyefcPrC0SdDUlJ2+B41P9c7beT4hLl3"],"proofs":[{"total":256,"index":2,"leaf_hash":"Mk/gSoEKQT0ZPL3KIer0UkpRdtkbx56XHiqqMaGVtrk=","aunts":["LYgYw+KhCIZcEkKSTff8Tv1ecvgBNAjaFNVjEHI/1V4=","2ZMrGbBti6vOpyA5+GwAiPG/WUrD8G0R+UadnuOgGnE=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":3,"leaf_hash":"LYgYw+KhCIZcEkKSTff8Tv1ecvgBNAjaFNVjEHI/1V4=","aunts":["Mk/gSoEKQT0ZPL3KIer0UkpRdtkbx56XHiqqMaGVtrk=","2ZMrGbBti6vOpyA5+GwAiPG/WUrD8G0R+UadnuOgGnE=","/C6uast+5NsKbWZ0466zj1c0k2zr+36QiDR70bqwA+A=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":4,"leaf_hash":"4tNMeqJI8hfcuqx4dyA+tslLe2+dqAIWql00DpyZKvA=","aunts":["GHOXHtloA0zAOgtirG7yO5dH9D0V+/dZOxjeYgF7q7Q=","f5uyCPRE2n5uwOiLK8Ttkla2LS0AMne5wAb8WOE2tec=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":5,"leaf_hash":"GHOXHtloA0zAOgtirG7yO5dH9D0V+/dZOxjeYgF7q7Q=","aunts":["4tNMeqJI8hfcuqx4dyA+tslLe2+dqAIWql00DpyZKvA=","f5uyCPRE2n5uwOiLK8Ttkla2LS0AMne5wAb8WOE2tec=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":2,"end_row":5},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":44,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTmUXx9Ejf7TqMry3Hc06txkRX/IjxaFFAJYVPg6jKw0Y","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTn862d4yiTl1j9zVBwuSlQInzE8nQKGjv3kJwGrdUiNj","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTqZS7I1+hDWPDlZGk3KyzusBUvlsmUKqFxz+dDiKg/Xn","/////////////////////////////////////////////////////////////////////////////88LEzpo3WHpvi5ozAu6b7v3v9VTUg6/ML55zUu6wMs7"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+5aN0XxgSMFm0qvKJF8yRo6bYJYrjITZ4/J7VwiEIYq"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////56llXjBDUdhpZJ4p/i3/OKWKHOJKxyBjIJOassgPD+w"],"is_max_namespace_ignored":true},{"end":23,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGBHjy8k2IRqzjYT37bpATwUmDUBojQ2wt3dKmj+8dw6n","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKNygA3gVPP/kDgNjfbgDJE7ZDWVq178GBU9kxlgzM04x","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKATkUvaFC/3+A/7WIEGqq5Cx1ZvAD8VtpCOAswcmPHGO","/////////////////////////////////////////////////////////////////////////////7WLsIvpJ52ieh1Hj2lSe/FsmtQ+F9gDlbg20HUx+qnr"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGOGGOP4lAb4Zwiu1IkuM2jIgLRLNbUY9LF/ksJIDFVZd","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGL95bbzuFM7pYC75Htey4Et1+A8wF249sUxewPe4KWqs","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGMR9mSY3bm3CLfLhUK3vNFnb1Xr9T98mezt82cEVM6t7","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKAwSNVok0TprlWx9njOFwbtrODIHvUUWEoTUacSpe6JW"],"proofs":[{"total":256,"index":15,"leaf_hash":"f9i2babDNafVo8XCm+6mglKjM0yvSQYzCGWVpRrZDG4=","aunts":["vjMFXUrjb9E/HnSDsHlqXchokirc0+ODJxvjIGNX63k=","GVEHSNKTEGN4LlmzUPUfoRho+V2LbXBfqkLTWWqAYUU=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":16,"leaf_hash":"r0i7TJEWXARtEtau8P9xtp+JZokbCZItTJdmH4PkIJE=","aunts":["6/Q/OMuaz2zAb5VAsu7w6XqrsJ8FdcVi2i5INi5EDLQ=","TqD2MTX3WGc2321/ta0lVSftXI382JgEH0DsC6j99rw=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":17,"leaf_hash":"6/Q/OMuaz2zAb5VAsu7w6XqrsJ8FdcVi2i5INi5EDLQ=","aunts":["r0i7TJEWXARtEtau8P9xtp+JZokbCZItTJdmH4PkIJE=","TqD2MTX3WGc2321/ta0lVSftXI382JgEH0DsC6j99rw=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":18,"leaf_hash":"IMGvE6gDhQ0wvbqEgLYbgCJc5DYiR0pDsSqFN8jI/nk=","aunts":["OY+4AvwKfn06lSYX67YsPpO3IleRyjiFeruuk2k6e5I=","WcdSCE3kkEE4eHW8LlKEiTOyJSQ7yChfRu7TQuUfXz8=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":15,"end_row":18},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":44,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTmUXx9Ejf7TqMry3Hc06txkRX/IjxaFFAJYVPg6jKw0Y","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTn862d4yiTl1j9zVBwuSlQInzE8nQKGjv3kJwGrdUiNj","AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAil7q59afqHbXTqZS7I1+hDWPDlZGk3KyzusBUvlsmUKqFxz+dDiKg/Xn","/////////////////////////////////////////////////////////////////////////////88LEzpo3WHpvi5ozAu6b7v3v9VTUg6/ML55zUu6wMs7"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+5aN0XxgSMFm0qvKJF8yRo6bYJYrjITZ4/J7VwiEIYq"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////56llXjBDUdhpZJ4p/i3/OKWKHOJKxyBjIJOassgPD+w"],"is_max_namespace_ignored":true},{"end":23,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGBHjy8k2IRqzjYT37bpATwUmDUBojQ2wt3dKmj+8dw6n","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKNygA3gVPP/kDgNjfbgDJE7ZDWVq178GBU9kxlgzM04x","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKATkUvaFC/3+A/7WIEGqq5Cx1ZvAD8VtpCOAswcmPHGO","/////////////////////////////////////////////////////////////////////////////7WLsIvpJ52ieh1Hj2lSe/FsmtQ+F9gDlbg20HUx+qnr"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIpe6ufWn6h2104AAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGOGGOP4lAb4Zwiu1IkuM2jIgLRLNbUY9LF/ksJIDFVZd","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGL95bbzuFM7pYC75Htey4Et1+A8wF249sUxewPe4KWqs","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAjx4jMG2LwUNEGMR9mSY3bm3CLfLhUK3vNFnb1Xr9T98mezt82cEVM6t7","AAAAAAAAAAAAAAAAAAAAAAAAAI8eIzBti8FDRBgAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKAwSNVok0TprlWx9njOFwbtrODIHvUUWEoTUacSpe6JW"],"proofs":[{"total":256,"index":15,"leaf_hash":"f9i2babDNafVo8XCm+6mglKjM0yvSQYzCGWVpRrZDG4=","aunts":["vjMFXUrjb9E/HnSDsHlqXchokirc0+ODJxvjIGNX63k=","GVEHSNKTEGN4LlmzUPUfoRho+V2LbXBfqkLTWWqAYUU=","lLVT5YmJ7zcPQOzC+7MtyXqdPpe7i+y0nzTQ9pTH7dY=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":16,"leaf_hash":"r0i7TJEWXARtEtau8P9xtp+JZokbCZItTJdmH4PkIJE=","aunts":["6/Q/OMuaz2zAb5VAsu7w6XqrsJ8FdcVi2i5INi5EDLQ=","TqD2MTX3WGc2321/ta0lVSftXI382JgEH0DsC6j99rw=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":17,"leaf_hash":"6/Q/OMuaz2zAb5VAsu7w6XqrsJ8FdcVi2i5INi5EDLQ=","aunts":["r0i7TJEWXARtEtau8P9xtp+JZokbCZItTJdmH4PkIJE=","TqD2MTX3WGc2321/ta0lVSftXI382JgEH0DsC6j99rw=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":18,"leaf_hash":"IMGvE6gDhQ0wvbqEgLYbgCJc5DYiR0pDsSqFN8jI/nk=","aunts":["OY+4AvwKfn06lSYX67YsPpO3IleRyjiFeruuk2k6e5I=","WcdSCE3kkEE4eHW8LlKEiTOyJSQ7yChfRu7TQuUfXz8=","8+OVEB1PYlE1f7MDuEl+piWsv74KQuoqVUCZl7f2mv0=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":15,"end_row":18},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":60,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKG/wfk+oxUAd9jp3nR/jvQhh8OSYLDnteyPZsxVEYiM7","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKAD+s6CVYC6HN/A5mfvzN/YB2M6v1NSv6o+roK7sLj52","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKIQGsvRspwRJtonTS04nOrGTUOHWUJoctS52yioQ/MIy","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKMUkz4u3eb5MmNAx78MtEakmgb1C0gxpTqX2X1BNKU+x","/////////////////////////////////////////////////////////////////////////////+dP/D4MdoGa22KND6HRKC6EjIv/m+Ui3D8dKmRHjBaK"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////74FP76wVnKOnVYdCqrPdQ9WP0wgwQ11ISOjgiLt9uwm"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////5j4E0WQGhAB5123Qe4o9QTSJLwNDw+QXuTcgRFaZd/"],"is_max_namespace_ignored":true},{"end":41,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnaoR7tsId4W3KBOKChZ5DeJNIk2jVIf0zD6mYVubrNc7H","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnarL0LntYHXF1tJmNiIuheLTtJkOkJDTd6RsJ6mggwaRf","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2JOohKPh0UzCn0hlFnKCo9+mrqyndHEG78IW2QHUWK3E","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2GWgNV/nYv12qIJGemqeLpI+gPW2fh6u0wTmaG88rTzO","/////////////////////////////////////////////////////////////////////////////xCEL5rjIalY0Uz0KMkhMmatTkE0bdypzkWlQEDonE0c"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anag==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anas8fQAYzdyUEO0t8ktNxv+4FrYxQSfeOn/lbX/1m0Rm7","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anai2V3m08fZea/KhlNEvEfTvLfCRMRn2/3RS+TCKnHra4","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnakjnCNzYE0+lqibEd7YX48Th79EITTeFthVCsu4VQrJX","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2Fi4FN5HMkHbswBS+ezJvNTxBnde87+8+VEDZwHkkpk5"],"proofs":[{"total":256,"index":20,"leaf_hash":"OBkuDEskGWb4cVfyORX7wEomJLL9NrXoaQZGsqNXauE=","aunts":["+GoVs3pwWV+2h5HDt8I1Bg9Lag9DZIuK1Rv0P+IUC+M=","FLrz0bqhHW+G5DvkfcZIZVSu9WnbPDET9hVSIUnIOYM=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":21,"leaf_hash":"+GoVs3pwWV+2h5HDt8I1Bg9Lag9DZIuK1Rv0P+IUC+M=","aunts":["OBkuDEskGWb4cVfyORX7wEomJLL9NrXoaQZGsqNXauE=","FLrz0bqhHW+G5DvkfcZIZVSu9WnbPDET9hVSIUnIOYM=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":22,"leaf_hash":"jZNkvkRKXMy8UwLO90eZRkd9p2NxrVvE4GGk3qTXm9Y=","aunts":["XwOnYkJHph0FFv2iYk+JYN0Fz3UzKnOEhzYR20BPFOs=","aKBVBrxZ5yQgWxVqXOm/7dYZwxeTbDKtrFxY6+A25qk=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":23,"leaf_hash":"XwOnYkJHph0FFv2iYk+JYN0Fz3UzKnOEhzYR20BPFOs=","aunts":["jZNkvkRKXMy8UwLO90eZRkd9p2NxrVvE4GGk3qTXm9Y=","aKBVBrxZ5yQgWxVqXOm/7dYZwxeTbDKtrFxY6+A25qk=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":20,"end_row":23},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":60,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKG/wfk+oxUAd9jp3nR/jvQhh8OSYLDnteyPZsxVEYiM7","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKAD+s6CVYC6HN/A5mfvzN/YB2M6v1NSv6o+roK7sLj52","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKIQGsvRspwRJtonTS04nOrGTUOHWUJoctS52yioQ/MIy","AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAr0/2LD/2qtieKMUkz4u3eb5MmNAx78MtEakmgb1C0gxpTqX2X1BNKU+x","/////////////////////////////////////////////////////////////////////////////+dP/D4MdoGa22KND6HRKC6EjIv/m+Ui3D8dKmRHjBaK"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////74FP76wVnKOnVYdCqrPdQ9WP0wgwQ11ISOjgiLt9uwm"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////5j4E0WQGhAB5123Qe4o9QTSJLwNDw+QXuTcgRFaZd/"],"is_max_namespace_ignored":true},{"end":41,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnaoR7tsId4W3KBOKChZ5DeJNIk2jVIf0zD6mYVubrNc7H","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnarL0LntYHXF1tJmNiIuheLTtJkOkJDTd6RsJ6mggwaRf","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2JOohKPh0UzCn0hlFnKCo9+mrqyndHEG78IW2QHUWK3E","AAAAAAAAAAAAAAAAAAAAAAAAAPRp11sg1lpUGNgAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2GWgNV/nYv12qIJGemqeLpI+gPW2fh6u0wTmaG88rTzO","/////////////////////////////////////////////////////////////////////////////xCEL5rjIalY0Uz0KMkhMmatTkE0bdypzkWlQEDonE0c"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anag==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAK9P9iw/9qrYnigAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anas8fQAYzdyUEO0t8ktNxv+4FrYxQSfeOn/lbX/1m0Rm7","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5Anai2V3m08fZea/KhlNEvEfTvLfCRMRn2/3RS+TCKnHra4","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAAxka1s8DQV5AnakjnCNzYE0+lqibEd7YX48Th79EITTeFthVCsu4VQrJX","AAAAAAAAAAAAAAAAAAAAAAAAAMZGtbPA0FeQJ2oAAAAAAAAAAAAAAAAAAAAAAAAA9GnXWyDWWlQY2Fi4FN5HMkHbswBS+ezJvNTxBnde87+8+VEDZwHkkpk5"],"proofs":[{"total":256,"index":20,"leaf_hash":"OBkuDEskGWb4cVfyORX7wEomJLL9NrXoaQZGsqNXauE=","aunts":["+GoVs3pwWV+2h5HDt8I1Bg9Lag9DZIuK1Rv0P+IUC+M=","FLrz0bqhHW+G5DvkfcZIZVSu9WnbPDET9hVSIUnIOYM=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":21,"leaf_hash":"+GoVs3pwWV+2h5HDt8I1Bg9Lag9DZIuK1Rv0P+IUC+M=","aunts":["OBkuDEskGWb4cVfyORX7wEomJLL9NrXoaQZGsqNXauE=","FLrz0bqhHW+G5DvkfcZIZVSu9WnbPDET9hVSIUnIOYM=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":22,"leaf_hash":"jZNkvkRKXMy8UwLO90eZRkd9p2NxrVvE4GGk3qTXm9Y=","aunts":["XwOnYkJHph0FFv2iYk+JYN0Fz3UzKnOEhzYR20BPFOs=","aKBVBrxZ5yQgWxVqXOm/7dYZwxeTbDKtrFxY6+A25qk=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":23,"leaf_hash":"XwOnYkJHph0FFv2iYk+JYN0Fz3UzKnOEhzYR20BPFOs=","aunts":["jZNkvkRKXMy8UwLO90eZRkd9p2NxrVvE4GGk3qTXm9Y=","aKBVBrxZ5yQgWxVqXOm/7dYZwxeTbDKtrFxY6+A25qk=","NhQP+8Esi6Z29sXFSm2hDqaFHsEXsyhPkQgjVy5Xoeo=","SpploEbSsAqj0T99TysKuxMA5RHOxPyFO6dNogCS55c=","BvmZismRolLXqyFUdqVjt67jQoXQAMgtIX/koPgZcAk=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":20,"end_row":23},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETP0Tx9WK7sAhwDitVsXIHXrwla3XwNLVj9CtQZdxcyx","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEaHcXv/yI3kyVX/SF44N97ZDFDEN7Bixr5lJryYAi0/y","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEcKmt5SVDlRQefcoyyfcpMlw4ap4LG/wYkqp9v0lmNNl","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVERmCWe8rQQm0ozNT9nl//27kBR80PuNKLRwjNz10VB2L","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEW+7X5BQxCVMR8L7d55bolQNKE25MGhEJHpJInxTN1Cf","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEdZGt4QDpQFRSFb7H6JEC7M+fyM54qSnzuar0vK3jVct","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVESEK97XNzPE07Uwclig98QSl0bmL8P+pS0h/nSLlHzV7","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETYfL9ZaIzoNERkFF49E1WrfV/Q24dbmTEQww5lASJSw","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEcaJ2HJI4t/EtXVtQjmHtegyTz8Mm1KHCZ4X/ky2hbYA","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEce5TEgLbXjI1ciFtd/DnPbt/AIpgWlg8WIkbu8+Um3y","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEWzOppW26yxHF6mTI3/JEKYVrpHquUQMsGyR8NnuwoGo","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVERN6WaKJr0Oh8rynbaDCvXk0qic3KZ3OfCX33hzbzIPe","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEd2PVOZdM/gopiu1Prigq1akjcVYb/xs1SO0+hWre9l0","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEZTvWtqAJNNNHGMKdH93fA4Du9RxHZQpH4MBDVZ/q2qK","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEYIr+BiFU3WPm/iP1aJOFzaXrofHKVH60emOjXlkR2X2","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEceCOwxYlGSg1LsK4g7txq5kN1hBneRZ1XIpR15AunP+","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEeZ9bHWVRC1bPD1KJA9QK+lBNcCMIGlqgc3QZxiF7bLt","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEXT/hJhue6+fBycx1/PdIl8FSYnR4jzF/Hfoi2ZmGumI","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEXalwbMfMCKdvUSYkKAcbjv1P6aIWnfjG46yZCGQKRKO","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEXL1fM5JVdhtqpue5JYswc0u5rr2nt2OPI5fthE7BW8r","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEZ73fzQh/b1UOQI6urktp6xGN0jpACEY+W6KLjgUWiKS","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEcalR36vftMp+vtu+0h4pLpWJDPLdcSEWGYxYP9YLWMz","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVESdxuD2+NMkydcjx8iPrZDBmrcizRthmg8TGH/gqYQkf","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEaYk2axjAK24GucaYkEU9cPExdvSX74VboNL0ePHJwP9","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVESYJ1wp1sUxohCFcTLYeoObQyUQNbaqBxP0/0TPv/uXM","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEbwVxPQntegdBaXaeYW4y0oFb8SB+DBifP8zV3QijM8a","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEYslYRauskA7nR/LZsGJvtH9f8r06TTp7dx/G2nfoMlq","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEUM6eVIWDH3SPbrWWQJlAnaOA1QTktaao+nvcBSyWgEF","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEc9uGzQjyX7KKqYM+dP+xSLd3YhQWdsEFh3UeufaEwd6","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEc8dYPBvNSkhQEeGzwWCMD3T25xXTKXp3FR2VkWgBLNQ","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEYunGuPe6w8Ihh+ONTHh1tjJ8G/l+tRfLe2o8gTgXR1W","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEcJecptRaC87KTBDN8dOcSQmeiEwNt3LsIShxLPy1fXs","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEabNXAPpDo6R+rsKtqSzWc3SOdevKRt1US0nI2GGpjov","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEW71wc+GC/0CO6iSY6F40uFK0drgXCuXVTNsNr1MqwkX","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEYFxv5tpMKIPUv8bKBtJKOo5i8YtopmGvC04vdI5jXE6","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEVcldxE8OplR3WtYEb078M/6zu/9gKN0fyFrEkc2FPbJ","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEYtieEdpe0JztpJlWrITSdp12RcuaOMDACfiTXuCRm9Q","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEcJ5MxQdGqhco2gOuPaP63RGYbzMC4O4jLJKIQTNUNOc","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEX5dcV+cQn8YVt0g7W7ZEP1fogaAw/KeUdJap4aBdOdV","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETQlz6cgp/F3ew4hix8g3NT/vhrs7VM6fwbQYm8XGXQu","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEQXkvuzrl+YqWVFJMENcnrxIDmbm34T7GNQhxb5hvV92","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETFCHAauxXjnXDuECisKyNhUrfarPbnw3szTiCB0f/fs","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEWu1Mfo/ncWYWSwrrYMMTDs9CMID68nsWqUJ6Bp1GA0Z","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEdtsyc1OQtQot6MoLakHcLBfw0Xh61J4/EZSm8mvgsWl","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEW1/MLqDr/Pium+5Rm/u2lW5ZQ5FrC6t9OyJK5TXGgQp"],"subtree_root_proofs":[{"start":20,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRd0hKOTmTId6sCUq+fgHe7ON2x4Scb6LXngT5j4bxoHa","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRUYlpqTrFNpgxe/6EzdToyiqQEMK7fYx5d8FywxNYp8O","/////////////////////////////////////////////////////////////////////////////5n6lv/xoiSafLA6LRjA5zklvw4nJbNZfFFUMxcuJkYg"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////6Cnj4X5a0tEWxdSlmbVGFkGEwr1lMRKjsH+ACNKVu5E"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////7mzNR/w5g8NQErKhSMDbvxYv50cJ/6P+JuoQhCnZvjb"],"is_max_namespace_ignored":true},{"end":3,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEU9ROQZZ94/xj/quqrY4xRlmejpJg05Qcbnkg/9x+6Ke","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1iBdQz3vZz2gPbNK7qzbMtQG5vpAnpJFnPNWqP4d1PBA","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1rOVYk/ilrtrVxLf7S3zylT94P9RwaijexB3OA0v7SGy","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1pcmXTaG+F8d4qH63h8NbyClygEvhdUB1WuxtpywenjH","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1oUU2LKmKutsVZeMLwKX9XjkxfBw0igmvrHcKOAU5U1E","/////////////////////////////////////////////////////////////////////////////xRCwJvU7MDpz4gfeJO25ZtnbVbzBwp00q1FmFfVWBlk"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETz77NxuFk3duyefcPrC0SdDUlJ2+B41P9c7beT4hLl3","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEWB00Btvq/k/xCP4i5+4KY5AgrBy6MAs4RkiOjWQlHB4","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEQir5/IeFsktw+z+kUPr0kAhzKngYHtxuR2lU9nm2Xov","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1nrlx1hsCDLV8aEJ+kyszqC6I6AYgPlEBpr/b+OA+NDY"],"proofs":[{"total":256,"index":5,"leaf_hash":"GHOXHtloA0zAOgtirG7yO5dH9D0V+/dZOxjeYgF7q7Q=","aunts":["4tNMeqJI8hfcuqx4dyA+tslLe2+dqAIWql00DpyZKvA=","f5uyCPRE2n5uwOiLK8Ttkla2LS0AMne5wAb8WOE2tec=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":6,"leaf_hash":"Kj8+v9xVV9xnnbgq37AuNmK7nLMu1GuA6wknEZL1Dqk=","aunts":["8TZGCJn8Ma16t1h2OnTUFd7InHHLa9trqLqhGvISZ1w=","duI1hPOklQ7tojAgQPHFWVbUUPd6ejIpS6VaxPoc77I=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":7,"leaf_hash":"8TZGCJn8Ma16t1h2OnTUFd7InHHLa9trqLqhGvISZ1w=","aunts":["Kj8+v9xVV9xnnbgq37AuNmK7nLMu1GuA6wknEZL1Dqk=","duI1hPOklQ7tojAgQPHFWVbUUPd6ejIpS6VaxPoc77I=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":8,"leaf_hash":"JH/ZifCts+YocCKYYHB9ohBDUved4FPhirkeY1ATH98=","aunts":["FbG0JjQmdVODTayyMWtxDXcZs+t1WtqXpm72K7wtbQA=","yO9x0X1Y5A+gYR0Xnfqsuajn5teS9yQkoHxzXIIBq6c=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":5,"end_row":8},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":20,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRd0hKOTmTId6sCUq+fgHe7ON2x4Scb6LXngT5j4bxoHa","AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAPzEBNl79AsXgRUYlpqTrFNpgxe/6EzdToyiqQEMK7fYx5d8FywxNYp8O","/////////////////////////////////////////////////////////////////////////////5n6lv/xoiSafLA6LRjA5zklvw4nJbNZfFFUMxcuJkYg"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////6Cnj4X5a0tEWxdSlmbVGFkGEwr1lMRKjsH+ACNKVu5E"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////7mzNR/w5g8NQErKhSMDbvxYv50cJ/6P+JuoQhCnZvjb"],"is_max_namespace_ignored":true},{"end":3,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEU9ROQZZ94/xj/quqrY4xRlmejpJg05Qcbnkg/9x+6Ke","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1iBdQz3vZz2gPbNK7qzbMtQG5vpAnpJFnPNWqP4d1PBA","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1rOVYk/ilrtrVxLf7S3zylT94P9RwaijexB3OA0v7SGy","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1pcmXTaG+F8d4qH63h8NbyClygEvhdUB1WuxtpywenjH","AAAAAAAAAAAAAAAAAAAAAAAAAEbcHUdyCG21GdYAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1oUU2LKmKutsVZeMLwKX9XjkxfBw0igmvrHcKOAU5U1E","/////////////////////////////////////////////////////////////////////////////xRCwJvU7MDpz4gfeJO25ZtnbVbzBwp00q1FmFfVWBlk"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAD8xATZe/QLF4EUAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVETz77NxuFk3duyefcPrC0SdDUlJ2+B41P9c7beT4hLl3","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEWB00Btvq/k/xCP4i5+4KY5AgrBy6MAs4RkiOjWQlHB4","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAAQnWgt3YaryAVEQir5/IeFsktw+z+kUPr0kAhzKngYHtxuR2lU9nm2Xov","AAAAAAAAAAAAAAAAAAAAAAAAAEJ1oLd2Gq8gFREAAAAAAAAAAAAAAAAAAAAAAAAARtwdR3IIbbUZ1nrlx1hsCDLV8aEJ+kyszqC6I6AYgPlEBpr/b+OA+NDY"],"proofs":[{"total":256,"index":5,"leaf_hash":"GHOXHtloA0zAOgtirG7yO5dH9D0V+/dZOxjeYgF7q7Q=","aunts":["4tNMeqJI8hfcuqx4dyA+tslLe2+dqAIWql00DpyZKvA=","f5uyCPRE2n5uwOiLK8Ttkla2LS0AMne5wAb8WOE2tec=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":6,"leaf_hash":"Kj8+v9xVV9xnnbgq37AuNmK7nLMu1GuA6wknEZL1Dqk=","aunts":["8TZGCJn8Ma16t1h2OnTUFd7InHHLa9trqLqhGvISZ1w=","duI1hPOklQ7tojAgQPHFWVbUUPd6ejIpS6VaxPoc77I=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":7,"leaf_hash":"8TZGCJn8Ma16t1h2OnTUFd7InHHLa9trqLqhGvISZ1w=","aunts":["Kj8+v9xVV9xnnbgq37AuNmK7nLMu1GuA6wknEZL1Dqk=","duI1hPOklQ7tojAgQPHFWVbUUPd6ejIpS6VaxPoc77I=","My88xeyfzkDbDlXF6jv8OKgL2gZkCFjQyZ6dK6rpXok=","Cy3TL8Y2rd87K1Y6bVUtMokzyuP4eCW4QeiqL/lG6qc=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]},{"total":256,"index":8,"leaf_hash":"JH/ZifCts+YocCKYYHB9ohBDUved4FPhirkeY1ATH98=","aunts":["FbG0JjQmdVODTayyMWtxDXcZs+t1WtqXpm72K7wtbQA=","yO9x0X1Y5A+gYR0Xnfqsuajn5teS9yQkoHxzXIIBq6c=","a6NAN9+G1VWjWIaY8XPWgvMfYe/0kIbj39zl7BzJ20Y=","1WjNVHCWRaA99LdADemA2HJgwiQ5qyRWp3KED1NFt4Y=","x8DyJ9Rb3qKR3DsRF2mKcvgWMd4k+EMQ7c3DRf16uXU=","+6G9XBDPZBMx+MGEyHvwOWL7ksSTY5xYgbSgEWLN6zM=","DTJoKQ3wCmPo9yJbfPOu2dk349RGhA+KjTuRxfbPRkU=","yuGDwJUQ1uVljrkYYtNCdn/qV/vCEaDScOCT35EgkpE="]}],"start_row":5,"end_row":8},"namespace_version":0},"root":"zYHXVH7+cpNW9J8qKYfEwoIlCdPsmPNgMdV1bJY6Dik=","sub_threshold":64}] \ No newline at end of file diff --git a/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e33303020736861726573.json b/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e33303020736861726573.json new file mode 100755 index 0000000000..8561b9a83d --- /dev/null +++ b/blob/testdata/fuzz-corpus/verify-6c6172676520626c6f6273207e33303020736861726573.json @@ -0,0 +1 @@ +[{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpj5XSRVUqpyeV7je1tJGgkcvTQyZlBhN7O3vLkFov8Nc","/////////////////////////////////////////////////////////////////////////////2ukxpjEnsJDdRXnSCvh+DEV3/NZZgl3aEhkmSgrnpLt"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8OfJu8FElr4dWfUp2ztDzKHNjsukLn0JZPlOowPJHPL"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3EtTL66sGJq86B2sSRIlSn+vvpm0uPQkHt1fEdGOBRg"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yXm9m+5qjrO7x98tRVcd/YwLN2FJESVkPyAes8A2zv7"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////qjud3cv9y94LKyYXqPx+O91RkB4vEyeMvMJdEXcV7u"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SXjRKMRtefvKPWAbmItGTs/Cir1tMpDKbIHBQbbcmO7p","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SSLC38ZJumZ3WmolzlUH+UDQqImX7a0adXclFt3KdGwk","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SW2wLD1mCrFI0X+VlfYUr1UVUf4CaPo+HttiJzva2Pgi","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SRlvmlsE9/nlRe7UiB9titeX/MEWzV9WzJbsRQ+u4jA0","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35ST2jHymwlx/wRl0LPXmGh4cuDo+27RfkYRyQA1+Jro//"],"proofs":[{"total":256,"index":5,"leaf_hash":"YiTj6EdtNOd08BmyxvcMZ1hFQMacpQDSeQYtGFQbgsM=","aunts":["ZQVrwfJgsPFV+0e057jVj36KAFlT3XrTwu8zZTdmAIE=","ImDVNo/SZKt7L8eLrGAwf3SVuDnR1Lpt45PdbEorFeA=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":6,"leaf_hash":"EZDpwQ0wtV6O72ot0GraLxqREwMYggMRg/m38ZFPbJU=","aunts":["l6J20Zun1a10XSK1Dt4CAM+aj0yYugItsaHkxHgi/KQ=","JtrAqvQzFp3EEu1Faslhi/E5eso//GgPZCGo9nQflw8=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":7,"leaf_hash":"l6J20Zun1a10XSK1Dt4CAM+aj0yYugItsaHkxHgi/KQ=","aunts":["EZDpwQ0wtV6O72ot0GraLxqREwMYggMRg/m38ZFPbJU=","JtrAqvQzFp3EEu1Faslhi/E5eso//GgPZCGo9nQflw8=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":8,"leaf_hash":"5ZKvHpiaMxMlHjRWn1MRyx8R3dnDH7e0I3xzWC0sbAQ=","aunts":["lbBuqLiNMxpi1u2GdvjmLNsYZe18G0+rxglGrHM85jQ=","lY6cfSPff7rV8R411pAZB/lOyJLEaa5wSeCrGmASSuo=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":9,"leaf_hash":"lbBuqLiNMxpi1u2GdvjmLNsYZe18G0+rxglGrHM85jQ=","aunts":["5ZKvHpiaMxMlHjRWn1MRyx8R3dnDH7e0I3xzWC0sbAQ=","lY6cfSPff7rV8R411pAZB/lOyJLEaa5wSeCrGmASSuo=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":5,"end_row":9},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpj5XSRVUqpyeV7je1tJGgkcvTQyZlBhN7O3vLkFov8Nc","/////////////////////////////////////////////////////////////////////////////2ukxpjEnsJDdRXnSCvh+DEV3/NZZgl3aEhkmSgrnpLt"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8OfJu8FElr4dWfUp2ztDzKHNjsukLn0JZPlOowPJHPL"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3EtTL66sGJq86B2sSRIlSn+vvpm0uPQkHt1fEdGOBRg"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yXm9m+5qjrO7x98tRVcd/YwLN2FJESVkPyAes8A2zv7"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////qjud3cv9y94LKyYXqPx+O91RkB4vEyeMvMJdEXcV7u"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SXjRKMRtefvKPWAbmItGTs/Cir1tMpDKbIHBQbbcmO7p","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SSLC38ZJumZ3WmolzlUH+UDQqImX7a0adXclFt3KdGwk","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SW2wLD1mCrFI0X+VlfYUr1UVUf4CaPo+HttiJzva2Pgi","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SRlvmlsE9/nlRe7UiB9titeX/MEWzV9WzJbsRQ+u4jA0","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35ST2jHymwlx/wRl0LPXmGh4cuDo+27RfkYRyQA1+Jro//"],"proofs":[{"total":256,"index":5,"leaf_hash":"YiTj6EdtNOd08BmyxvcMZ1hFQMacpQDSeQYtGFQbgsM=","aunts":["ZQVrwfJgsPFV+0e057jVj36KAFlT3XrTwu8zZTdmAIE=","ImDVNo/SZKt7L8eLrGAwf3SVuDnR1Lpt45PdbEorFeA=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":6,"leaf_hash":"EZDpwQ0wtV6O72ot0GraLxqREwMYggMRg/m38ZFPbJU=","aunts":["l6J20Zun1a10XSK1Dt4CAM+aj0yYugItsaHkxHgi/KQ=","JtrAqvQzFp3EEu1Faslhi/E5eso//GgPZCGo9nQflw8=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":7,"leaf_hash":"l6J20Zun1a10XSK1Dt4CAM+aj0yYugItsaHkxHgi/KQ=","aunts":["EZDpwQ0wtV6O72ot0GraLxqREwMYggMRg/m38ZFPbJU=","JtrAqvQzFp3EEu1Faslhi/E5eso//GgPZCGo9nQflw8=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":8,"leaf_hash":"5ZKvHpiaMxMlHjRWn1MRyx8R3dnDH7e0I3xzWC0sbAQ=","aunts":["lbBuqLiNMxpi1u2GdvjmLNsYZe18G0+rxglGrHM85jQ=","lY6cfSPff7rV8R411pAZB/lOyJLEaa5wSeCrGmASSuo=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":9,"leaf_hash":"lbBuqLiNMxpi1u2GdvjmLNsYZe18G0+rxglGrHM85jQ=","aunts":["5ZKvHpiaMxMlHjRWn1MRyx8R3dnDH7e0I3xzWC0sbAQ=","lY6cfSPff7rV8R411pAZB/lOyJLEaa5wSeCrGmASSuo=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":5,"end_row":9},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":24,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SaM2c7TMYySCmSYvHyQFP6lSqEH+rQu+6s2E5FNpAhR3","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SZErb49hUf7qqqKpsgKA+5z7btdmYbvfkDSY95xwvi9X","/////////////////////////////////////////////////////////////////////////////1SpImIuLEvvVQrYneBKAb0a5J9C/Ephd5gSavQ/85n0"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////xJpebUaruqtcuGNVVDudEhdkC834IMIrjZRtG/Jx4r1"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2Y3+orcO6/v3osrINeY+e7eZl5/Cd4HxShLePfdOpYP"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////7JzxEdbC2QF4HENYXkc8w968PHNGcfhHBy1vghXsf4G"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8xe6VNTa8uQtRW/sVgydDFfWK4xQ4WzhD8tbVrYhYPU"],"is_max_namespace_ignored":true},{"end":18,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4D0quw09zlsErmeY3PK6rZfqNE4U9sOXB5uVSjjoMCiN","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4D6q1EjBwRDpZ87Lgkfy9JwqqWFOcTlcrin00A0mfGth","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBVyZov04fwc8NWNCKNomG87wrXckWIOVlzMYxAypKQrv6","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV8dPZuTHxeIiRtwb9TyaaSLLGJGARMO3vjmZ9ERox3eG","/////////////////////////////////////////////////////////////////////////////+Ta4UfRBz3O4c/OYGnRnHhHyboGVuGb6AuVHekEHuHc"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4KvH8vCqKOf51LNW5xGibAVy5Ksz0QbWrHoDDLbRUR5v","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4GnKOcTKqJqJxpbD3+aYv8DddqAS3ba+XRPOjdOKwt4D","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4AA3IyTot0BNdJGbpz6PYZUvQEm6/AxV9TjxR4bAumrP","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4C7orNK5auf9jtsGK91mQeFqq4pUcG4hA63hF+xeGCEz","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4JnYoGBBNs5Unnpe9pjX+pVaoV7LBo/VM51ZbUJr2/6z","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV5Esq7Q/6+rAZF0NjCTwbkX8PEC63i8EQNmLAV9rvbkS"],"proofs":[{"total":256,"index":30,"leaf_hash":"BJPKbLGHTF+uwRJvks9DtO82Df+CwZeAW8hHbWirK/Q=","aunts":["SCZu6CK6r/Urxzf3BTv4Z23a51W0Rsv9PLRvo7pF+g4=","d+LG2YyYnl0254cVn8A6Z6c3JCkxETILii/aAb06JvA=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":31,"leaf_hash":"SCZu6CK6r/Urxzf3BTv4Z23a51W0Rsv9PLRvo7pF+g4=","aunts":["BJPKbLGHTF+uwRJvks9DtO82Df+CwZeAW8hHbWirK/Q=","d+LG2YyYnl0254cVn8A6Z6c3JCkxETILii/aAb06JvA=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":32,"leaf_hash":"fnNwFs6M48O6OXY1dF7EtMGByCi4/ePvm8pyCjRHlDc=","aunts":["K8vR2IffW47Z2mq5+LRMZMqRqM/2ohCOKXDnfnXtox4=","E27D4Vv7/eTdiAjYA58h7QyTCjSnnxQZgw3abynA0gk=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":33,"leaf_hash":"K8vR2IffW47Z2mq5+LRMZMqRqM/2ohCOKXDnfnXtox4=","aunts":["fnNwFs6M48O6OXY1dF7EtMGByCi4/ePvm8pyCjRHlDc=","E27D4Vv7/eTdiAjYA58h7QyTCjSnnxQZgw3abynA0gk=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":34,"leaf_hash":"zKADZJzH8HOXFruUmEdx9+A13ST0EmUImkMxZI8hFjg=","aunts":["oDQUdvrwakVZi17pSEJLsuPv6+d6KDF0PlVgEaX369M=","6eoLn08K4MfRbMEOm8AOCG7zxFfOofIPsAR678yKl9U=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":35,"leaf_hash":"oDQUdvrwakVZi17pSEJLsuPv6+d6KDF0PlVgEaX369M=","aunts":["zKADZJzH8HOXFruUmEdx9+A13ST0EmUImkMxZI8hFjg=","6eoLn08K4MfRbMEOm8AOCG7zxFfOofIPsAR678yKl9U=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":30,"end_row":35},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4L+wcxq/xRT1KItDvRAjXunc2RCQpuU+bKGwC/BW0e2d","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4D72O1xYdY88H3FUg20JnG36Hn3iV+hAQugy0K8inhFy","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4BKFrcLx1BpnHKaXdAehQhJLRNkwFTaDbTfPthXGxCHb","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4EH7oFxDTleU7RmR36G8C0jPghpUBGQe/Sf4nIEtCWFj","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4MwKDsEONgovnAmg6SCbXJSKP2z9H6zC0RFmU2gnr2Uw","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4NNtZ45qK0gKAzsLMZFgeOYK1SxPCtudYeoOS8LR9uno","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4IMKpdCNGmWYfe6/gaZRek0y0PKA48CLW82rmbDSCgC9","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4FDnIatLdFAF7Ap8gY4Lh0NKHmhf01JrK4CM7zGxjMow","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4KCVPyKMDHiSBK0d5e58dZKMQHW0casGe6Ip1u8KgtaG","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4K5fbgOhNbET5qm5QeewwdpLGEYMoyTUL6EXPhkeYO2f","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4DLw/Auz62WlAOcNNICDG4YybgvcB9aT0Ycd4CnBRzL6","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4PMA88jQJocGdsJisWvXGmUHae9+N5wO61JEMu+b98iy","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4PSYiMU12vk/UiYMthbQDOFz6BYl2o1OUGDY4Ls3V8/i","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4M34uAwMfocY4u1lJvjseXKTpWew7JW090rrx4l59pHO","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4EYKF89EHLb6ViXoJH3Elx+NatXxx0lnkyjKJvmlzHko","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4BVrr1ZEszZzM6lkgA+LxZd1sVCNR3nO2OCKJFZbwCXg","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4Hi/vWjldpjl0ykeEhRgYnG0kqLSgnP08B4U5wxDUjPB","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4PBnmTjYPf/DeQt1F3wKZdG8VnGDHwrGT+D55812TvRU","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4LducXF44X0hSH6FKGPbefTeFR3k1NjfvGHEXueXtkbr","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4DXUgAcWZGKE+FQTFTz1abPXktmk5Vj8AceiR4GZ9v0/","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4C7R1J6eMvP3KdQV6dPU6sOWfzCVecnLTNgHy0UJmpvI","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4AXx5ea7T5ICKLe7BzaE+OWEUGxFs1Rw2emAycx2kKNw","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4EgEtXnMF8mcOxYKsZYhSomR7LAjTmyqHfiJw/qta2UH","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4ILFm0yV332N01PoH0z98W1hZDvKNE2ofvsAc0PY5HbK","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4KK/xWryMg7N/eUyA4IeUQcZ8ON+jk/bSbxm1T3UM9l2","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4FuXUY+qUTmfvX71iEZHP4+DIjjYWwYaF5GMruVb8QIH","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4LKLnXIa0HAOIo0ojvS2HKWhG8tMX58LEorDWXbtr3pn","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4MYmvRtadYaNRT8SLA3tEBK21X468RIzLYl/a0mJZcjo","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4FANH4rY9JdHnTC1kKlm+p/y1y6r2T3ecrpFtC2vTC6X","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4LzESnJYiu++h1MGuaF1RFvpooX3Vl2fzfGxHlgljL+4","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4P0geRdFQyBeNYr0pKd9kS1zdEfey8iHut9Yn22jbGMO","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4LQFYwHDXLYKaOYuXt3uPiONdDtB44F/pF0unmZHGPRe","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4EqNHqDo0m6SqrU5nSlUhGFt8WDtzyGpmp/ilh1BfMV/","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4Le86YZy2jtivA9s8MjCwvGJ9j4zgTF/ZLB1XgV8IUoO","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4Oi4TeJqLzmhsknKfV8LQwmil4QH7Imn1YCjKydA4D5t","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4EWBKtA78O/fMd6DcM1szQB5AhNTPOy/qlwdP2FGyn1/","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4LHgreg7ETpUNSbPfUX/879N5Q3Lp0NNcYdZjAIAj+H7","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4MDRz5Xii7lzf2+jF3UPcfjdkzeZf0H3oVjIrHRlyKZQ","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4AwPwtDETfQP3SUA0GfQdEocJ79GJ1qivNpCtr4fzEHT","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4CvzAyoanzGrqhwE2j/WTX8cgz+jXUr7ckm6kuFC58gG"],"subtree_root_proofs":[{"start":24,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SaM2c7TMYySCmSYvHyQFP6lSqEH+rQu+6s2E5FNpAhR3","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SZErb49hUf7qqqKpsgKA+5z7btdmYbvfkDSY95xwvi9X","/////////////////////////////////////////////////////////////////////////////1SpImIuLEvvVQrYneBKAb0a5J9C/Ephd5gSavQ/85n0"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////xJpebUaruqtcuGNVVDudEhdkC834IMIrjZRtG/Jx4r1"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2Y3+orcO6/v3osrINeY+e7eZl5/Cd4HxShLePfdOpYP"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////7JzxEdbC2QF4HENYXkc8w968PHNGcfhHBy1vghXsf4G"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8xe6VNTa8uQtRW/sVgydDFfWK4xQ4WzhD8tbVrYhYPU"],"is_max_namespace_ignored":true},{"end":18,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4D0quw09zlsErmeY3PK6rZfqNE4U9sOXB5uVSjjoMCiN","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4D6q1EjBwRDpZ87Lgkfy9JwqqWFOcTlcrin00A0mfGth","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBVyZov04fwc8NWNCKNomG87wrXckWIOVlzMYxAypKQrv6","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV8dPZuTHxeIiRtwb9TyaaSLLGJGARMO3vjmZ9ERox3eG","/////////////////////////////////////////////////////////////////////////////+Ta4UfRBz3O4c/OYGnRnHhHyboGVuGb6AuVHekEHuHc"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4KvH8vCqKOf51LNW5xGibAVy5Ksz0QbWrHoDDLbRUR5v","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4GnKOcTKqJqJxpbD3+aYv8DddqAS3ba+XRPOjdOKwt4D","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4AA3IyTot0BNdJGbpz6PYZUvQEm6/AxV9TjxR4bAumrP","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4C7orNK5auf9jtsGK91mQeFqq4pUcG4hA63hF+xeGCEz","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4JnYoGBBNs5Unnpe9pjX+pVaoV7LBo/VM51ZbUJr2/6z","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV5Esq7Q/6+rAZF0NjCTwbkX8PEC63i8EQNmLAV9rvbkS"],"proofs":[{"total":256,"index":30,"leaf_hash":"BJPKbLGHTF+uwRJvks9DtO82Df+CwZeAW8hHbWirK/Q=","aunts":["SCZu6CK6r/Urxzf3BTv4Z23a51W0Rsv9PLRvo7pF+g4=","d+LG2YyYnl0254cVn8A6Z6c3JCkxETILii/aAb06JvA=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":31,"leaf_hash":"SCZu6CK6r/Urxzf3BTv4Z23a51W0Rsv9PLRvo7pF+g4=","aunts":["BJPKbLGHTF+uwRJvks9DtO82Df+CwZeAW8hHbWirK/Q=","d+LG2YyYnl0254cVn8A6Z6c3JCkxETILii/aAb06JvA=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":32,"leaf_hash":"fnNwFs6M48O6OXY1dF7EtMGByCi4/ePvm8pyCjRHlDc=","aunts":["K8vR2IffW47Z2mq5+LRMZMqRqM/2ohCOKXDnfnXtox4=","E27D4Vv7/eTdiAjYA58h7QyTCjSnnxQZgw3abynA0gk=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":33,"leaf_hash":"K8vR2IffW47Z2mq5+LRMZMqRqM/2ohCOKXDnfnXtox4=","aunts":["fnNwFs6M48O6OXY1dF7EtMGByCi4/ePvm8pyCjRHlDc=","E27D4Vv7/eTdiAjYA58h7QyTCjSnnxQZgw3abynA0gk=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":34,"leaf_hash":"zKADZJzH8HOXFruUmEdx9+A13ST0EmUImkMxZI8hFjg=","aunts":["oDQUdvrwakVZi17pSEJLsuPv6+d6KDF0PlVgEaX369M=","6eoLn08K4MfRbMEOm8AOCG7zxFfOofIPsAR678yKl9U=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":35,"leaf_hash":"oDQUdvrwakVZi17pSEJLsuPv6+d6KDF0PlVgEaX369M=","aunts":["zKADZJzH8HOXFruUmEdx9+A13ST0EmUImkMxZI8hFjg=","6eoLn08K4MfRbMEOm8AOCG7zxFfOofIPsAR678yKl9U=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":30,"end_row":35},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV+gS3K37xx0nbkj/i46ODyBFUoRPQCy/jgKG/a93uZqq","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBVz1oeiY8hXGhWbIgV/4drs1Vp8etf9hoNwI+v7aqL/2E","/////////////////////////////////////////////////////////////////////////////19iYOFBgfbCxJ5BIfEBFwEAqCWgWKlNtv72f5GjQbHS"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2JEfvnT/qqUCzllO+TsbTl1aJlEZoAMGNB2IMcYIy+R"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8U75TPEBlu1f0HCNGKhLZfc81NBusoAtlylXzpBHryZ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////w3+xdrLzyuZ2z5+GeaOwDlC5Xm9YncGynTB+4FaQ5Vl"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////72pTJu7SMYZlvuY9OXtcDgn6dAgg0QWS8LBJ5KTRfwa"],"is_max_namespace_ignored":true},{"end":36,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw598WGYXkICKitxDwmvhmmbzTMYBJk01o9zLalA3AHr8d","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYH58e9rRcjPtlmLXPDNskmkAdpjucfeOvZwvW0d9K3fW","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYADZ0qD2wbNt8WDcZVRkqCFIZU/TZd6b5Cxk0+tzYjIl","/////////////////////////////////////////////////////////////////////////////xF0xgnLfzB1CcMdgxmkI6FgnmbD0C7GxyL8PHqax+78"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw5w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw56RleFZoLpkVb4EarkZgpIf2PHCRsSqdNOGDXMbCWf+t","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw58sE8rJP5MneAb3A+mdkD3hUMmS9KiMAQlKFUUFCkzLu","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw576nzTFWcYeBuKP0kVAxGRh2JnCqHqr+kFRUbLM8mlpi","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw51UJu7EfAZr+9PoykkeDtrWHqMGDsQb3Vn+YlPM6MMyn","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw55bGwMyPedW5MutzJEnaXzXm7nOOQr+FUcaZcc6l3R64","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYKfXNY34XlDHW9SqQE1QwM/GZq/FqUM0rFthRqm6fFXr"],"proofs":[{"total":256,"index":40,"leaf_hash":"BeymHfpJwuN1DEltW7EQYZ2Cwb5YmhzPi+Z8SlnwmPA=","aunts":["puC/o1wmeYSbUK8SKljanN+JYKEffGlvZ1vnG0hpLwI=","/hH0Tf0JU91CiqsP3qWZAPRTOs42ha9bkyi1+AJGIvo=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":41,"leaf_hash":"puC/o1wmeYSbUK8SKljanN+JYKEffGlvZ1vnG0hpLwI=","aunts":["BeymHfpJwuN1DEltW7EQYZ2Cwb5YmhzPi+Z8SlnwmPA=","/hH0Tf0JU91CiqsP3qWZAPRTOs42ha9bkyi1+AJGIvo=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":42,"leaf_hash":"J6lsIQKDuSPoS3ZvR5B5B+5iHoVoF8Zy9Y8GnBF43eo=","aunts":["Q9arCWDGhMtvBceRNNG97Qv1tiA9wDXxagBICzPaliw=","TrUU+6G+KRdI3ZsoM+NFNdC3PNsSBNU/PYhdFhcPdr0=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":43,"leaf_hash":"Q9arCWDGhMtvBceRNNG97Qv1tiA9wDXxagBICzPaliw=","aunts":["J6lsIQKDuSPoS3ZvR5B5B+5iHoVoF8Zy9Y8GnBF43eo=","TrUU+6G+KRdI3ZsoM+NFNdC3PNsSBNU/PYhdFhcPdr0=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":44,"leaf_hash":"r52gHjdGOgYKzvKhztrUQsz7WJ7CVnO6dW/y/2P2Bo0=","aunts":["DrzO6G5fO5JqCKdiVXPwHYSl2n0vZBGR8gcg1LWpBuQ=","bexsUeu3vWCUJPdYKdBwe661vrKtfvOhWdQWp3N9wuA=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":45,"leaf_hash":"DrzO6G5fO5JqCKdiVXPwHYSl2n0vZBGR8gcg1LWpBuQ=","aunts":["r52gHjdGOgYKzvKhztrUQsz7WJ7CVnO6dW/y/2P2Bo0=","bexsUeu3vWCUJPdYKdBwe661vrKtfvOhWdQWp3N9wuA=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":40,"end_row":45},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV+gS3K37xx0nbkj/i46ODyBFUoRPQCy/jgKG/a93uZqq","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBVz1oeiY8hXGhWbIgV/4drs1Vp8etf9hoNwI+v7aqL/2E","/////////////////////////////////////////////////////////////////////////////19iYOFBgfbCxJ5BIfEBFwEAqCWgWKlNtv72f5GjQbHS"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2JEfvnT/qqUCzllO+TsbTl1aJlEZoAMGNB2IMcYIy+R"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8U75TPEBlu1f0HCNGKhLZfc81NBusoAtlylXzpBHryZ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////w3+xdrLzyuZ2z5+GeaOwDlC5Xm9YncGynTB+4FaQ5Vl"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////72pTJu7SMYZlvuY9OXtcDgn6dAgg0QWS8LBJ5KTRfwa"],"is_max_namespace_ignored":true},{"end":36,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw598WGYXkICKitxDwmvhmmbzTMYBJk01o9zLalA3AHr8d","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYH58e9rRcjPtlmLXPDNskmkAdpjucfeOvZwvW0d9K3fW","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYADZ0qD2wbNt8WDcZVRkqCFIZU/TZd6b5Cxk0+tzYjIl","/////////////////////////////////////////////////////////////////////////////xF0xgnLfzB1CcMdgxmkI6FgnmbD0C7GxyL8PHqax+78"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw5w==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw56RleFZoLpkVb4EarkZgpIf2PHCRsSqdNOGDXMbCWf+t","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw58sE8rJP5MneAb3A+mdkD3hUMmS9KiMAQlKFUUFCkzLu","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw576nzTFWcYeBuKP0kVAxGRh2JnCqHqr+kFRUbLM8mlpi","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw51UJu7EfAZr+9PoykkeDtrWHqMGDsQb3Vn+YlPM6MMyn","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw55bGwMyPedW5MutzJEnaXzXm7nOOQr+FUcaZcc6l3R64","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYKfXNY34XlDHW9SqQE1QwM/GZq/FqUM0rFthRqm6fFXr"],"proofs":[{"total":256,"index":40,"leaf_hash":"BeymHfpJwuN1DEltW7EQYZ2Cwb5YmhzPi+Z8SlnwmPA=","aunts":["puC/o1wmeYSbUK8SKljanN+JYKEffGlvZ1vnG0hpLwI=","/hH0Tf0JU91CiqsP3qWZAPRTOs42ha9bkyi1+AJGIvo=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":41,"leaf_hash":"puC/o1wmeYSbUK8SKljanN+JYKEffGlvZ1vnG0hpLwI=","aunts":["BeymHfpJwuN1DEltW7EQYZ2Cwb5YmhzPi+Z8SlnwmPA=","/hH0Tf0JU91CiqsP3qWZAPRTOs42ha9bkyi1+AJGIvo=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":42,"leaf_hash":"J6lsIQKDuSPoS3ZvR5B5B+5iHoVoF8Zy9Y8GnBF43eo=","aunts":["Q9arCWDGhMtvBceRNNG97Qv1tiA9wDXxagBICzPaliw=","TrUU+6G+KRdI3ZsoM+NFNdC3PNsSBNU/PYhdFhcPdr0=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":43,"leaf_hash":"Q9arCWDGhMtvBceRNNG97Qv1tiA9wDXxagBICzPaliw=","aunts":["J6lsIQKDuSPoS3ZvR5B5B+5iHoVoF8Zy9Y8GnBF43eo=","TrUU+6G+KRdI3ZsoM+NFNdC3PNsSBNU/PYhdFhcPdr0=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":44,"leaf_hash":"r52gHjdGOgYKzvKhztrUQsz7WJ7CVnO6dW/y/2P2Bo0=","aunts":["DrzO6G5fO5JqCKdiVXPwHYSl2n0vZBGR8gcg1LWpBuQ=","bexsUeu3vWCUJPdYKdBwe661vrKtfvOhWdQWp3N9wuA=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":45,"leaf_hash":"DrzO6G5fO5JqCKdiVXPwHYSl2n0vZBGR8gcg1LWpBuQ=","aunts":["r52gHjdGOgYKzvKhztrUQsz7WJ7CVnO6dW/y/2P2Bo0=","bexsUeu3vWCUJPdYKdBwe661vrKtfvOhWdQWp3N9wuA=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":40,"end_row":45},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":16,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2D9tYhIzRWqI6o0Nzyl21OXgVJQbup5fskP+BuYvf4Kv","/////////////////////////////////////////////////////////////////////////////6nMXyXRR+OJyDHsNETab9ca4GnMF9LwhfQJl92BEgmv"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////xZljiKZ8Ovm0R6+uyiZ9ZLHBeJlM1dTmwfpvtxRBN9q"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2SeFyV0GvViy5oV7mjISpeweOQH/DYpSnqdnLnCqI0g"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////xbh47pijZXfbQjJqdHLUKXRHqrmrXvGRWB1z3ysXQcq"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////zhks4mvH5bJjhPlp3p0B7yXD3tmFRC9Dtw5ExM2iHDZ"],"is_max_namespace_ignored":true},{"end":14,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI84Bjp2umU5WXrO8VDYorSy9L2MBBU4IFAmaVuxJFrYe","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SS2C4Svawod9q3aCUrgoLMua1bGuwjit865wXZ/kzocQ","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SX1SH0TII1i1n9rWwTqmMVZH/87SbSJaVaw24O8z9GW1","/////////////////////////////////////////////////////////////////////////////+ceKPG/LhyB/I9mPXZIV7lHdziBt4lr24wbgTJmyzRk"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI/2mSsT1/9kQk4EYoD/fqo3+itSCtRkSh00gKpCelNP0","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI23hoDaXuXIeKUO+jVavggFLC2/x4Y0KgkwUyDxlu6il","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI0pZQpvy12vJGDuhg+yX+M1ohber0kbR5b5aGK4ogWWY","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI01UMY4EqzyxoF8PyuhpOQQXZK6X6kdYhUHxJLShAWYa","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI+7VnQ0zoYfurxA6RmfFCGWyclBD92cbcAA394GxQJL5","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SR5/xT1NyxqlpeQUlGNqoEvjNeF466w1HwE0xHhcd3Do"],"proofs":[{"total":256,"index":20,"leaf_hash":"VVQ3+ZqC6h+6Tei0ilGNRcZhDsZkoV/EgpsMlXpUyZA=","aunts":["Bt7YlmNy0MkPU0i2z3PBzsYDcAEr/YTtAqjMTx0i1ao=","oeUD1ZmdfQx4TCswDDAGjMc/0jjJpPHdxFNBV0FufO4=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":21,"leaf_hash":"Bt7YlmNy0MkPU0i2z3PBzsYDcAEr/YTtAqjMTx0i1ao=","aunts":["VVQ3+ZqC6h+6Tei0ilGNRcZhDsZkoV/EgpsMlXpUyZA=","oeUD1ZmdfQx4TCswDDAGjMc/0jjJpPHdxFNBV0FufO4=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":22,"leaf_hash":"Q52uyRol3s7f9q2kGFuezDWsl2VE66d/fGYbtBRKDus=","aunts":["cLi3+Izn/MvHT8eRA9Un2M2Kvc2cxKr5ta0E34eWvmU=","BzUuq8sCCDGZDYMCbpUbQ0VfavcpxSJmgVGhZzrx+oE=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":23,"leaf_hash":"cLi3+Izn/MvHT8eRA9Un2M2Kvc2cxKr5ta0E34eWvmU=","aunts":["Q52uyRol3s7f9q2kGFuezDWsl2VE66d/fGYbtBRKDus=","BzUuq8sCCDGZDYMCbpUbQ0VfavcpxSJmgVGhZzrx+oE=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":24,"leaf_hash":"wrgVNSfPC7SrdYbSGXTUPNry6ZkXpJKHOX1Gm1V7XKs=","aunts":["AsSjqjR6t7DoYUApYX3+UzxBWjfemRmDZEqaTmru1vw=","w7aYDssyRcFBgbC9kUW4iAqInDC8n5L+kOrX7Ja4+yI=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":25,"leaf_hash":"AsSjqjR6t7DoYUApYX3+UzxBWjfemRmDZEqaTmru1vw=","aunts":["wrgVNSfPC7SrdYbSGXTUPNry6ZkXpJKHOX1Gm1V7XKs=","w7aYDssyRcFBgbC9kUW4iAqInDC8n5L+kOrX7Ja4+yI=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":20,"end_row":25},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIw2M/dxTPyLrp2LxLur30g8KuK5DEmmoKJ72Up2OoGAa","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI2vMHOzhOG5rMu0zRHs8QAvaWwsq4kb84svF6P5DxMuU","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI8TUk0wwQvWK/ZX6rPMp/IAWemOx+iSKgAi2NmLrd6xh","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI8yi/KnBmAS4ISbW2enVtOPwIKHI/UoHH/R+hS2+LicN","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI0Meufjth4v1rX4B6eZZ8L9ttfA6dKWgQ4e8msQnFfzM","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI5Vybok0oLTAn/93sUcuzVu4U7SUiIVfH2sH4S90H/Vc","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI13pOA4qh0xTQ3DM6GDMKXJxUeTzI7Vu/wvdrTn5rZRK","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI6X838vxdzd6kOe4mSLQSIIAOET8cbdToxqXDLNSNllx","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI5dwtpgmDN5jYbfnUicvyYjOCEeAmJza8P8Lt0z4qBFN","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI1zp8q8B1RUERY8ZIkX6UX9+HTNKUG5jBw5j05tUfaCw","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI+1IcbUpCVMDq/dL6tan8kRDSs+BHsg7XrFK86VkwQ1i","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI35LMyVpxVKO0/FRaT3oTZ+f5r6ZMCZSoxe/0J4FRgyZ","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIzhP9uN2ZPsWzrselM/eGKzbI69PwD7Z8U6TXcVMQAlW","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI5JEJIYIbeg70NN+iaafG8Hk7kOttKnY724Ak0VBlcJf","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIw2KORCFhsnjatp4qcja0Ldpt20wqenTRnpx0ah1A7Ag","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI9uo8rXcSOA3ZvtS1DF/c9NaX4hmgjbunE9BrQlPbaOc","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI9h0R4m2dECeHlYusqWalDFGH97CC7rLSM1uYe+ytV93","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI96kuZuUvdxkfZ9OpVECTdFhlpHmc6c0LsBoGZbuaZcY","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI5rJfBSmOLVh5vIV2T0m0ui0PZU1HXb4TNYV/4QfGxXj","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI1NPLJcnC9E5ZtewdFtCXqqXUqRasJTHJeakrbykiQ0h","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI/3d0mrqoa58LOT/wBqSXzGeOpHkQSvge+S620UJnhGK","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI77e40V477fYry90Ix3zQ2taaLp5YcMyrbFCS7a8DqAY","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI3b9//kIaNDTLU0UPXvhz+h/3+HDZxGxKf6Z/+IoG5LD","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI76aasr7kl6bG5hPd13xylNGT2P3J0L02yKxYnq+rw0i","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI7ji4oDsfWV46Y6qbP16Z0qd1ip1erX03syhlgF5Pyw+","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIyeNW9KR5lcqe631t8aK01Sno7Kcq2HhohJYi58/gu9A","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI8dg+AS7qnUUmu6nUc1jDy5KSW/RUmqjNCCYhnPEARoP","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI4jSu9QYrUxyQqMTPVZLsRnrCXb7A4qwn5HMp+kUxuj8","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIzcUC2ejeA5K7An03KUNEyJjbCIbjOWS1fFPr7R/yZ/U","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI7mysLP+QSMqv17gHpx2+E2B57eRQi3QH4XSgw3CwCQH","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI4ZoCb86WgxTBzcdzKIpqhGEgyE+Bb2BqNVbdSXoSS2b","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI9MS6ESM+Q/oNlpF4IAKDNrxdIoA35yAZYE85qcFburu","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIxdNF2kW5rFE9Utt81BCityI1O1kZDF3qowgBKy4DlTz","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIydRRnJjcBjdcApaQ3YvYfuqmzmKxGvXDUg3WeUl6ckq","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIyIvW/JV+O8TTvERGje7ZgJD/j/Kny8AMni20VeEyvmD","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI7Kx1CiTR3bYev1jBxWUqv8Re+uPSEz/XVs7ZF73gkDv","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI6uufAmWKr3AIObcr788S3C9+xkYn3OrpPXCPfNg9brH","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI4KhBqzSFdEQglzjW9YqpDWKp35M95idlb81dOC6N8N3","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI4Il7nHhYqsvNUxjzLggGiLLGSwbw4QMQjvAlRpx6XQh","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI9a2TFoU4Ia7a2ZAS7+B9VyIIFBD3UYjYRNqYJTl83KR","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI9zFq+4h847iiO2YO7mU3My0uEXND899OqSUByezlurp"],"subtree_root_proofs":[{"start":16,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2D9tYhIzRWqI6o0Nzyl21OXgVJQbup5fskP+BuYvf4Kv","/////////////////////////////////////////////////////////////////////////////6nMXyXRR+OJyDHsNETab9ca4GnMF9LwhfQJl92BEgmv"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////xZljiKZ8Ovm0R6+uyiZ9ZLHBeJlM1dTmwfpvtxRBN9q"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////2SeFyV0GvViy5oV7mjISpeweOQH/DYpSnqdnLnCqI0g"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////xbh47pijZXfbQjJqdHLUKXRHqrmrXvGRWB1z3ysXQcq"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////zhks4mvH5bJjhPlp3p0B7yXD3tmFRC9Dtw5ExM2iHDZ"],"is_max_namespace_ignored":true},{"end":14,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI84Bjp2umU5WXrO8VDYorSy9L2MBBU4IFAmaVuxJFrYe","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SS2C4Svawod9q3aCUrgoLMua1bGuwjit865wXZ/kzocQ","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SX1SH0TII1i1n9rWwTqmMVZH/87SbSJaVaw24O8z9GW1","/////////////////////////////////////////////////////////////////////////////+ceKPG/LhyB/I9mPXZIV7lHdziBt4lr24wbgTJmyzRk"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI/2mSsT1/9kQk4EYoD/fqo3+itSCtRkSh00gKpCelNP0","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI23hoDaXuXIeKUO+jVavggFLC2/x4Y0KgkwUyDxlu6il","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI0pZQpvy12vJGDuhg+yX+M1ohber0kbR5b5aGK4ogWWY","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI01UMY4EqzyxoF8PyuhpOQQXZK6X6kdYhUHxJLShAWYa","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI+7VnQ0zoYfurxA6RmfFCGWyclBD92cbcAA394GxQJL5","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SR5/xT1NyxqlpeQUlGNqoEvjNeF466w1HwE0xHhcd3Do"],"proofs":[{"total":256,"index":20,"leaf_hash":"VVQ3+ZqC6h+6Tei0ilGNRcZhDsZkoV/EgpsMlXpUyZA=","aunts":["Bt7YlmNy0MkPU0i2z3PBzsYDcAEr/YTtAqjMTx0i1ao=","oeUD1ZmdfQx4TCswDDAGjMc/0jjJpPHdxFNBV0FufO4=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":21,"leaf_hash":"Bt7YlmNy0MkPU0i2z3PBzsYDcAEr/YTtAqjMTx0i1ao=","aunts":["VVQ3+ZqC6h+6Tei0ilGNRcZhDsZkoV/EgpsMlXpUyZA=","oeUD1ZmdfQx4TCswDDAGjMc/0jjJpPHdxFNBV0FufO4=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":22,"leaf_hash":"Q52uyRol3s7f9q2kGFuezDWsl2VE66d/fGYbtBRKDus=","aunts":["cLi3+Izn/MvHT8eRA9Un2M2Kvc2cxKr5ta0E34eWvmU=","BzUuq8sCCDGZDYMCbpUbQ0VfavcpxSJmgVGhZzrx+oE=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":23,"leaf_hash":"cLi3+Izn/MvHT8eRA9Un2M2Kvc2cxKr5ta0E34eWvmU=","aunts":["Q52uyRol3s7f9q2kGFuezDWsl2VE66d/fGYbtBRKDus=","BzUuq8sCCDGZDYMCbpUbQ0VfavcpxSJmgVGhZzrx+oE=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":24,"leaf_hash":"wrgVNSfPC7SrdYbSGXTUPNry6ZkXpJKHOX1Gm1V7XKs=","aunts":["AsSjqjR6t7DoYUApYX3+UzxBWjfemRmDZEqaTmru1vw=","w7aYDssyRcFBgbC9kUW4iAqInDC8n5L+kOrX7Ja4+yI=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":25,"leaf_hash":"AsSjqjR6t7DoYUApYX3+UzxBWjfemRmDZEqaTmru1vw=","aunts":["wrgVNSfPC7SrdYbSGXTUPNry6ZkXpJKHOX1Gm1V7XKs=","w7aYDssyRcFBgbC9kUW4iAqInDC8n5L+kOrX7Ja4+yI=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":20,"end_row":25},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/38OuIy5GCo3yyiqEiggpmqXOYgJByv6sti9Cqde6fVT","/////////////////////////////////////////////////////////////////////////////6XQCCOyHd/tdIg+FUjDyuRsSomQ+4V1NOrq27A5QGzu"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////6wNLMj/cbeqdL1sAI3MYcbv6jf+suTrKHFOcAhho5jP"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8KT+etm6FCoo6pyKNJMcJNXXeK5zTj//wbqJ2FF3XUO"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////9X5TU2JfCVFnRp03Vr5/7nn1bqpxnwZDRUubXV4ec2j"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////1nAZWlcWXYASx1xHF23XFnHjSLwL7X0QSx39T0vuqfX"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SXtl10LaaSzm2RQ6VxGrh5wd8jO8YDYjO4mQbQ3NgoSv","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SfpghpIo1r/hGbDWxtIGg0hxX++feZftAhViYsov0ReZ","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SUjwgQh5wGkxSMJxUEQ7GH4tbFReR0/IVB20ZQYjziAk","/////////////////////////////////////////////////////////////////////////////2ukxpjEnsJDdRXnSCvh+DEV3/NZZgl3aEhkmSgrnpLt"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpn75YK8CaueTUqd5umIbOExPBsUhSc/fXC32L7T3DMZ+","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpmz2OfbwAfWcXe5QhdsrBJPTyO2WQShy75tDis5CSKjp","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpngWv5PJkHy1dbEJlInJuaaLVRcP3qRjsg4vddBYivKU","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpuFZMGzlrMFhqNGtX1bfwNrBRWj0WDs221iEuSDLnnto","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpgg5hEX0R1GcFG+ZhmUrNMKUhnac81aiEHs4kJb70Nit","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SXjRKMRtefvKPWAbmItGTs/Cir1tMpDKbIHBQbbcmO7p"],"proofs":[{"total":256,"leaf_hash":"nf94kP+c2Z061KsvOHfPKDnI5vc6Ifiz6iufImSEGAk=","aunts":["jU7Mw7nDYYJx/5zrVfdBQiLxdoR3tZznRR95+Nx9vCw=","fAwvnPXdAkKwCgfQyivKnU51Nzp4NRjriCavNcQs/i8=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":1,"leaf_hash":"jU7Mw7nDYYJx/5zrVfdBQiLxdoR3tZznRR95+Nx9vCw=","aunts":["nf94kP+c2Z061KsvOHfPKDnI5vc6Ifiz6iufImSEGAk=","fAwvnPXdAkKwCgfQyivKnU51Nzp4NRjriCavNcQs/i8=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":2,"leaf_hash":"etz4YlV1R38C4XRzvjHyfuT7I4ADcbBnSk7v2uW7P80=","aunts":["hDM+J7PyqTm5tMDYW5Q7Xy4Q8hOAO3UjuM0MWHu+ftY=","R/RsPg+YKhpb3PgELQOcSH+6u0urGEgcSfkud+waLBc=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":3,"leaf_hash":"hDM+J7PyqTm5tMDYW5Q7Xy4Q8hOAO3UjuM0MWHu+ftY=","aunts":["etz4YlV1R38C4XRzvjHyfuT7I4ADcbBnSk7v2uW7P80=","R/RsPg+YKhpb3PgELQOcSH+6u0urGEgcSfkud+waLBc=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":4,"leaf_hash":"ZQVrwfJgsPFV+0e057jVj36KAFlT3XrTwu8zZTdmAIE=","aunts":["YiTj6EdtNOd08BmyxvcMZ1hFQMacpQDSeQYtGFQbgsM=","ImDVNo/SZKt7L8eLrGAwf3SVuDnR1Lpt45PdbEorFeA=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":5,"leaf_hash":"YiTj6EdtNOd08BmyxvcMZ1hFQMacpQDSeQYtGFQbgsM=","aunts":["ZQVrwfJgsPFV+0e057jVj36KAFlT3XrTwu8zZTdmAIE=","ImDVNo/SZKt7L8eLrGAwf3SVuDnR1Lpt45PdbEorFeA=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"end_row":5},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/38OuIy5GCo3yyiqEiggpmqXOYgJByv6sti9Cqde6fVT","/////////////////////////////////////////////////////////////////////////////6XQCCOyHd/tdIg+FUjDyuRsSomQ+4V1NOrq27A5QGzu"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////6wNLMj/cbeqdL1sAI3MYcbv6jf+suTrKHFOcAhho5jP"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////8KT+etm6FCoo6pyKNJMcJNXXeK5zTj//wbqJ2FF3XUO"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////9X5TU2JfCVFnRp03Vr5/7nn1bqpxnwZDRUubXV4ec2j"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////1nAZWlcWXYASx1xHF23XFnHjSLwL7X0QSx39T0vuqfX"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SXtl10LaaSzm2RQ6VxGrh5wd8jO8YDYjO4mQbQ3NgoSv","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SfpghpIo1r/hGbDWxtIGg0hxX++feZftAhViYsov0ReZ","AAAAAAAAAAAAAAAAAAAAAAAAAFzZhjjbexEN+UkAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SUjwgQh5wGkxSMJxUEQ7GH4tbFReR0/IVB20ZQYjziAk","/////////////////////////////////////////////////////////////////////////////2ukxpjEnsJDdRXnSCvh+DEV3/NZZgl3aEhkmSgrnpLt"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpn75YK8CaueTUqd5umIbOExPBsUhSc/fXC32L7T3DMZ+","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpmz2OfbwAfWcXe5QhdsrBJPTyO2WQShy75tDis5CSKjp","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpngWv5PJkHy1dbEJlInJuaaLVRcP3qRjsg4vddBYivKU","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpuFZMGzlrMFhqNGtX1bfwNrBRWj0WDs221iEuSDLnnto","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAMcmdpY11wJqWpgg5hEX0R1GcFG+ZhmUrNMKUhnac81aiEHs4kJb70Nit","AAAAAAAAAAAAAAAAAAAAAAAAADHJnaWNdcCalqYAAAAAAAAAAAAAAAAAAAAAAAAAXNmGONt7EQ35SXjRKMRtefvKPWAbmItGTs/Cir1tMpDKbIHBQbbcmO7p"],"proofs":[{"total":256,"leaf_hash":"nf94kP+c2Z061KsvOHfPKDnI5vc6Ifiz6iufImSEGAk=","aunts":["jU7Mw7nDYYJx/5zrVfdBQiLxdoR3tZznRR95+Nx9vCw=","fAwvnPXdAkKwCgfQyivKnU51Nzp4NRjriCavNcQs/i8=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":1,"leaf_hash":"jU7Mw7nDYYJx/5zrVfdBQiLxdoR3tZznRR95+Nx9vCw=","aunts":["nf94kP+c2Z061KsvOHfPKDnI5vc6Ifiz6iufImSEGAk=","fAwvnPXdAkKwCgfQyivKnU51Nzp4NRjriCavNcQs/i8=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":2,"leaf_hash":"etz4YlV1R38C4XRzvjHyfuT7I4ADcbBnSk7v2uW7P80=","aunts":["hDM+J7PyqTm5tMDYW5Q7Xy4Q8hOAO3UjuM0MWHu+ftY=","R/RsPg+YKhpb3PgELQOcSH+6u0urGEgcSfkud+waLBc=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":3,"leaf_hash":"hDM+J7PyqTm5tMDYW5Q7Xy4Q8hOAO3UjuM0MWHu+ftY=","aunts":["etz4YlV1R38C4XRzvjHyfuT7I4ADcbBnSk7v2uW7P80=","R/RsPg+YKhpb3PgELQOcSH+6u0urGEgcSfkud+waLBc=","osqGXyRi1u9VCl+yYwSCumXDalnK6E5YDjWYXkWhOEU=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":4,"leaf_hash":"ZQVrwfJgsPFV+0e057jVj36KAFlT3XrTwu8zZTdmAIE=","aunts":["YiTj6EdtNOd08BmyxvcMZ1hFQMacpQDSeQYtGFQbgsM=","ImDVNo/SZKt7L8eLrGAwf3SVuDnR1Lpt45PdbEorFeA=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":5,"leaf_hash":"YiTj6EdtNOd08BmyxvcMZ1hFQMacpQDSeQYtGFQbgsM=","aunts":["ZQVrwfJgsPFV+0e057jVj36KAFlT3XrTwu8zZTdmAIE=","ImDVNo/SZKt7L8eLrGAwf3SVuDnR1Lpt45PdbEorFeA=","ERZUnC5tSd50UTqfnHoG5b179Xqgq7Iys2o2xpOia5U=","GRUTfzNdALVshFTw2frnb3v0rftE5+/u2khfrg7R03c=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"end_row":5},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3JoEZroxfZEh4S6OFbewYFUgoumPF0MHQnhv6HUBYYLxj","/////////////////////////////////////////////////////////////////////////////9pVz8oTpS+7dp3plg5KRT711oMkchWmlgiqEQcfR8fC"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////39y+Qg3373AIqbD3SQJL3T+3X+xDpyW+UkZZ00Zn2+U"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////90248inxbUaWcDiwl7DkqQm1lR/vb4YHzOul4qR5rEp"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+QdpifihHx4JLm/KAIk1MD1jzFND5rP0Jm6ChJQk+EG"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////wsJBc6mhA19qNsV2v+CEUtoiHyW8GDPiTCdF0p90U0T"],"is_max_namespace_ignored":true},{"end":10,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2Ep0QyAMVn0QqRIvaqvXJWHB/zqNd48V8Wa8wGZJn/Lx","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2HaTSHb+Xa9PcBOQ1YV4A8EiY0/lUHHo82R4ORnU7SB6","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI0d6GJFlYAlyJWIPcOyhnXFsHmNwNHF8trQV36tSkbyI","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI7hnnr09pDnD0eDEhddQuh/uW7vrkzuE26oOm5xEiLLN","/////////////////////////////////////////////////////////////////////////////6nMXyXRR+OJyDHsNETab9ca4GnMF9LwhfQJl92BEgmv"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2M66vSQ56Xw0SDsh32fNm2hWSYJi0yYbIhcWihB46O16","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2ObQRy/RdXuPMiFYWW3eAg5myl4FsOqdq8uIKgzavce8","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2NOWr3LlVvbv6Z4JlKFqNDSoEq6FkuohI3yCedhRsbqU","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2PuCWKzIh75vu+cPMBW5YHVQqnxqEQyu8lmLzloJszz7","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2OBAdgCmow6KuQzCvwKbpV+CJQ6RvSwNJUdW6IW/r5bK","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI/2mSsT1/9kQk4EYoD/fqo3+itSCtRkSh00gKpCelNP0"],"proofs":[{"total":256,"index":15,"leaf_hash":"6Gkv2kuFcKYR2RpQNhVNgIi/bWLp2hbTgla9h8x1qQs=","aunts":["0T+TjTyrJ6BwUvrbGn6FxPgMZgAB50RbdGgGiwb7AF8=","o9+jJWZ1e5cIVA9aTeoVHFIxwIyqRRUynoXzxHFnZlk=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":16,"leaf_hash":"shw3htyjBQuKA1Vz3eVB2IjLDTaLKCQ/FSV2Dlfe9QU=","aunts":["5wHeZWGdpNwpbVz5FYKFUiX/71lWc5/jRqPpyIKafVQ=","OvVTx6F4QCed/XMm6Y3dkdlg7ayqx8aUlx/ESwv5mfw=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":17,"leaf_hash":"5wHeZWGdpNwpbVz5FYKFUiX/71lWc5/jRqPpyIKafVQ=","aunts":["shw3htyjBQuKA1Vz3eVB2IjLDTaLKCQ/FSV2Dlfe9QU=","OvVTx6F4QCed/XMm6Y3dkdlg7ayqx8aUlx/ESwv5mfw=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":18,"leaf_hash":"309p1CP03EnZhq1MMFxM5dPW7K38WFeNXdP2MvAUbK4=","aunts":["MnZbB/PQLxJaJhX/R3q7G8H5TEoWCqwk6fVfofAtEIg=","YJ/QWDPLTLhWOhyZMOuVEINXT3wEk8FBNlr6U5MqM6I=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":19,"leaf_hash":"MnZbB/PQLxJaJhX/R3q7G8H5TEoWCqwk6fVfofAtEIg=","aunts":["309p1CP03EnZhq1MMFxM5dPW7K38WFeNXdP2MvAUbK4=","YJ/QWDPLTLhWOhyZMOuVEINXT3wEk8FBNlr6U5MqM6I=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":20,"leaf_hash":"VVQ3+ZqC6h+6Tei0ilGNRcZhDsZkoV/EgpsMlXpUyZA=","aunts":["Bt7YlmNy0MkPU0i2z3PBzsYDcAEr/YTtAqjMTx0i1ao=","oeUD1ZmdfQx4TCswDDAGjMc/0jjJpPHdxFNBV0FufO4=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":15,"end_row":20},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2K9DOOCxSU92Gx+fNm9du/W9sFvMw+iLKmC2I6hRMqIX","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2DywNl2I0KvYraOVeD/tW/ucPndcIIRl0d291sDinDFm","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2F9OksPQTTCDyK4F5fjTKXHCmMOLUTxVEjEo5pQzOBCH","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2Ke4xivm3h+heI3EyL5LMYJ1eG0rMSgOC6TLjTp2DTOe","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2HzAxeqq6jHoaGJrzjkgIHx5mcV3zOnNr3SPZLCt/pHt","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2C5tZ1gHQYdM3+nyQSfxDjJSVDIoGdidlV2HlvlhQhwR","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2OXFNNfD/V+omLoXV+5YWSvYog6gYbn0QAMCYvrVl8K7","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2JGowJqsHBjhJ5FX5x10sq465ikctPV5v0rDVhgk+z+Z","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2GEFPqBV/b5HKqBhXQez3ocuRnWZkAOINre7yvKvSE/2","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2DJ/vm0sBTEW8JGQok1C2y5Fpqur/l15wJJMB07McRaX","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2Py36dm9KzzLaG9hPd++3ZfKkZc7sgfIi06NrZ1JKXAR","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2GdSwVqu4lZvX/TSjYoOPYVBNlPT3U4cw1UR7yXxXJwM","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2C/ftEw9lZocCgKS9AyB2pRQ0QBCLuoXzN5Gv/aGWjNJ","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2Fy90tyQdPATtZxq6FyvOf3oX8V9OKJYna0DV6HNfoNH","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2JuFATMSR/6LDZugj5l8iOGDElRgu0jpuPeWPec3+ja5","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2DH55nE5OnEizRxPowZku4y9Su8aYa3FNOHn/0zvPp4L","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2AFW6W8LxCjn8oMXYSikI+Mtl+gp9ap5RT6RQrty7erY","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2MwJCjmxdUNM0pO4SrknjUxaNarEic7YYzylBx1eF+2x","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2Igj7qYpIn5gsniRp83LpBr5X/sUFEFxSJKYjoNYqhos","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2GdX0y2p8cCY212wYN4YOX8kd9EQHoBLL7ZQbl69PpPh","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2F4/G5Ubm0aT7JN/p3Go/ojz65Owy60X1fsyPbFiFYeA","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2Fk3o0ksRSujeDBxpZ4E2EjaoavcPBQuoWGfVVAx/BI/","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2EviJeTFjA7vb9gWSChCVCuJD15QhPgJ+crD/knqgmp0","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2N7ZnJwsv3DR0SD1Z2BE58zcvLpy84fwCfTjLY1RwFtx","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2IWGJn9JKYtWAj4sSeZa7SFi4OqnDVUzCa/mpiFwGLcb","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2M96rA3tZKP6dBtaeX3nOA8DJdhCb8U1BwSzYZ1IoUhk","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2ET/aAH0YkxVkhCWLWylIdf9ZOTUeL7G9PPWrE+eKeBu","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2L1QFbWl/Iaf7Rl0a704c4upnHlGogV/Mrl7doFDamEI","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2LekqxsRZZWX1x4YRWqPiGe6EERYogb/mgmH861Xhy+t","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2PEivvZFhgRZXkbJOTST4pf1vPRlwbq/xh0m8vA2PBDj","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2OoMuo6NYJ6tN3yMICj1fC72tBsMtcdIAGhrqASmSn/i","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2FVOwICV/RdbUbGblrRtKjuwYZp7sxRgmdY1L6XeI8Dg","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2DqnTc52gb9imQ4kP7cTNn7avAiqZYSV1zJy4JOI2VsJ","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2IH1WdHObNF2u1Pc31wWh7fMs80d4fRPjdapmOUM1AMK","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2HFbt5m9ASM7Vz0um/o+uFmSn1aQLlcIo1NwD2LZEGuT","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2FsmONWLdqhGl4ND6+Cyf7vTsZNsOwQJZhxOhPjYs+PT","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2BEv9ApKkfd4Anc4leOXE4bgQcNqWKufUdA/ldl6++hM","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2MhzEnvurFzzGw2/0Qmx1sY06UAWQ1QdLt2aaKHS9gAs","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2IgUOgak5wyYGW6tnjKi8Wt8SGeVowxVlX6/Un7m9OXR","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2GR7LEHYbDTqjykMLQorJYG12usVNAasvEQ49q+1HxLG","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2BXjTrdx52zeU3mvZ3scCGoV3IBu2i7/f87RA5ccFOji"],"subtree_root_proofs":[{"start":8,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3JoEZroxfZEh4S6OFbewYFUgoumPF0MHQnhv6HUBYYLxj","/////////////////////////////////////////////////////////////////////////////9pVz8oTpS+7dp3plg5KRT711oMkchWmlgiqEQcfR8fC"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////39y+Qg3373AIqbD3SQJL3T+3X+xDpyW+UkZZ00Zn2+U"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////90248inxbUaWcDiwl7DkqQm1lR/vb4YHzOul4qR5rEp"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////+QdpifihHx4JLm/KAIk1MD1jzFND5rP0Jm6ChJQk+EG"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////wsJBc6mhA19qNsV2v+CEUtoiHyW8GDPiTCdF0p90U0T"],"is_max_namespace_ignored":true},{"end":10,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2Ep0QyAMVn0QqRIvaqvXJWHB/zqNd48V8Wa8wGZJn/Lx","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2HaTSHb+Xa9PcBOQ1YV4A8EiY0/lUHHo82R4ORnU7SB6","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI0d6GJFlYAlyJWIPcOyhnXFsHmNwNHF8trQV36tSkbyI","AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI7hnnr09pDnD0eDEhddQuh/uW7vrkzuE26oOm5xEiLLN","/////////////////////////////////////////////////////////////////////////////6nMXyXRR+OJyDHsNETab9ca4GnMF9LwhfQJl92BEgmv"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2A==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2M66vSQ56Xw0SDsh32fNm2hWSYJi0yYbIhcWihB46O16","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2ObQRy/RdXuPMiFYWW3eAg5myl4FsOqdq8uIKgzavce8","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2NOWr3LlVvbv6Z4JlKFqNDSoEq6FkuohI3yCedhRsbqU","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2PuCWKzIh75vu+cPMBW5YHVQqnxqEQyu8lmLzloJszz7","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2OBAdgCmow6KuQzCvwKbpV+CJQ6RvSwNJUdW6IW/r5bK","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrI/2mSsT1/9kQk4EYoD/fqo3+itSCtRkSh00gKpCelNP0"],"proofs":[{"total":256,"index":15,"leaf_hash":"6Gkv2kuFcKYR2RpQNhVNgIi/bWLp2hbTgla9h8x1qQs=","aunts":["0T+TjTyrJ6BwUvrbGn6FxPgMZgAB50RbdGgGiwb7AF8=","o9+jJWZ1e5cIVA9aTeoVHFIxwIyqRRUynoXzxHFnZlk=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":16,"leaf_hash":"shw3htyjBQuKA1Vz3eVB2IjLDTaLKCQ/FSV2Dlfe9QU=","aunts":["5wHeZWGdpNwpbVz5FYKFUiX/71lWc5/jRqPpyIKafVQ=","OvVTx6F4QCed/XMm6Y3dkdlg7ayqx8aUlx/ESwv5mfw=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":17,"leaf_hash":"5wHeZWGdpNwpbVz5FYKFUiX/71lWc5/jRqPpyIKafVQ=","aunts":["shw3htyjBQuKA1Vz3eVB2IjLDTaLKCQ/FSV2Dlfe9QU=","OvVTx6F4QCed/XMm6Y3dkdlg7ayqx8aUlx/ESwv5mfw=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":18,"leaf_hash":"309p1CP03EnZhq1MMFxM5dPW7K38WFeNXdP2MvAUbK4=","aunts":["MnZbB/PQLxJaJhX/R3q7G8H5TEoWCqwk6fVfofAtEIg=","YJ/QWDPLTLhWOhyZMOuVEINXT3wEk8FBNlr6U5MqM6I=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":19,"leaf_hash":"MnZbB/PQLxJaJhX/R3q7G8H5TEoWCqwk6fVfofAtEIg=","aunts":["309p1CP03EnZhq1MMFxM5dPW7K38WFeNXdP2MvAUbK4=","YJ/QWDPLTLhWOhyZMOuVEINXT3wEk8FBNlr6U5MqM6I=","+Je/DUWkL0/lY+3p/NqV6LfSnkj0G2wt20p6LjFUhhE=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":20,"leaf_hash":"VVQ3+ZqC6h+6Tei0ilGNRcZhDsZkoV/EgpsMlXpUyZA=","aunts":["Bt7YlmNy0MkPU0i2z3PBzsYDcAEr/YTtAqjMTx0i1ao=","oeUD1ZmdfQx4TCswDDAGjMc/0jjJpPHdxFNBV0FufO4=","8M+g/kC9wTFzW7FIOR7OPgz35ZLJ6DOyF/9Ph7sIU6w=","7e4HBIn5Rv8Tmf8BuuUzqi6ziavMHyrZAgg9YbIh9XU=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":15,"end_row":20},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":16,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIwWqANngOtpcnG288ReiYvTt3CdH2bxEd4Ibksh1xEf1","/////////////////////////////////////////////////////////////////////////////+ceKPG/LhyB/I9mPXZIV7lHdziBt4lr24wbgTJmyzRk"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////x7J2kr+IpjpV/73G1SCff0PgDiOm3tV//DLVeckh04c"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////32niX4E0ONH0rPM/qWM6mtMirP4kZrrB6jmyCsNaCTm"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3gLb+XlhiXWdsxjMMXh21U3bSd4jCS0qkzlK1UeORpK"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////wybOLqugCyASD0fSmonbk5ogc2raiZctD4hFSoa2peL"],"is_max_namespace_ignored":true},{"end":20,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SUGKUC8JtxWa2h5+zOLIpWuGYYvtJXTXynWTxK9Imgv0","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4L+wcxq/xRT1KItDvRAjXunc2RCQpuU+bKGwC/BW0e2d","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4OcAA/AW9l2jNd/uRCc74Z+aO9RGUJN7yYvzH9Vl/CMN","/////////////////////////////////////////////////////////////////////////////1SpImIuLEvvVQrYneBKAb0a5J9C/Ephd5gSavQ/85n0"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SR5/xT1NyxqlpeQUlGNqoEvjNeF466w1HwE0xHhcd3Do","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SZIOfZi0GRuqBPRkDAc//bN3aIoX8g/kAj40ap9LvgVt","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SUSGC//gEzisduQ18GLMlfOSz1GHNmBGp9uI9UaDg2wa","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SSMCVw0wCUU+lJFJwAoPqKRibMw3rHAJDhzxkXYPocWX","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/Sde/dwSmDWBZMEsu5iAc4cJFPO2BGUvi+t1hQNkr3uUh","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4KvH8vCqKOf51LNW5xGibAVy5Ksz0QbWrHoDDLbRUR5v"],"proofs":[{"total":256,"index":25,"leaf_hash":"AsSjqjR6t7DoYUApYX3+UzxBWjfemRmDZEqaTmru1vw=","aunts":["wrgVNSfPC7SrdYbSGXTUPNry6ZkXpJKHOX1Gm1V7XKs=","w7aYDssyRcFBgbC9kUW4iAqInDC8n5L+kOrX7Ja4+yI=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":26,"leaf_hash":"hsH77sEx7KiptABg/5ua2sTv99hwzslBCWSW7o25Rys=","aunts":["aGfFvzoQ6JnKTI3uhuzz34dPpRsptmNUEJIc27djAgM=","Zv/+K21F/3UNmzJ0PTPt6GWaF2JCrDKeBNEH7SBh1Jg=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":27,"leaf_hash":"aGfFvzoQ6JnKTI3uhuzz34dPpRsptmNUEJIc27djAgM=","aunts":["hsH77sEx7KiptABg/5ua2sTv99hwzslBCWSW7o25Rys=","Zv/+K21F/3UNmzJ0PTPt6GWaF2JCrDKeBNEH7SBh1Jg=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":28,"leaf_hash":"DCEKoftPvQN1/NIRcgkrDmMfuE17s3ESiBCvuaZKz0Q=","aunts":["4bdIMilhceFya6whT0/Bu6a5KMJZ/Flz9ZIPhcMnr5I=","+m7i6F0vrDq0QIYK6zjposSkJH+BysRyA4Riv/4mPA8=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":29,"leaf_hash":"4bdIMilhceFya6whT0/Bu6a5KMJZ/Flz9ZIPhcMnr5I=","aunts":["DCEKoftPvQN1/NIRcgkrDmMfuE17s3ESiBCvuaZKz0Q=","+m7i6F0vrDq0QIYK6zjposSkJH+BysRyA4Riv/4mPA8=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":30,"leaf_hash":"BJPKbLGHTF+uwRJvks9DtO82Df+CwZeAW8hHbWirK/Q=","aunts":["SCZu6CK6r/Urxzf3BTv4Z23a51W0Rsv9PLRvo7pF+g4=","d+LG2YyYnl0254cVn8A6Z6c3JCkxETILii/aAb06JvA=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":25,"end_row":30},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":16,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAlQNyJh9JAmWrIwWqANngOtpcnG288ReiYvTt3CdH2bxEd4Ibksh1xEf1","/////////////////////////////////////////////////////////////////////////////+ceKPG/LhyB/I9mPXZIV7lHdziBt4lr24wbgTJmyzRk"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////x7J2kr+IpjpV/73G1SCff0PgDiOm3tV//DLVeckh04c"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////32niX4E0ONH0rPM/qWM6mtMirP4kZrrB6jmyCsNaCTm"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3gLb+XlhiXWdsxjMMXh21U3bSd4jCS0qkzlK1UeORpK"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////wybOLqugCyASD0fSmonbk5ogc2raiZctD4hFSoa2peL"],"is_max_namespace_ignored":true},{"end":20,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SUGKUC8JtxWa2h5+zOLIpWuGYYvtJXTXynWTxK9Imgv0","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4L+wcxq/xRT1KItDvRAjXunc2RCQpuU+bKGwC/BW0e2d","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4OcAA/AW9l2jNd/uRCc74Z+aO9RGUJN7yYvzH9Vl/CMN","/////////////////////////////////////////////////////////////////////////////1SpImIuLEvvVQrYneBKAb0a5J9C/Ephd5gSavQ/85n0"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SQ==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAJUDciYfSQJlqyMAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SR5/xT1NyxqlpeQUlGNqoEvjNeF466w1HwE0xHhcd3Do","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SZIOfZi0GRuqBPRkDAc//bN3aIoX8g/kAj40ap9LvgVt","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SUSGC//gEzisduQ18GLMlfOSz1GHNmBGp9uI9UaDg2wa","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/SSMCVw0wCUU+lJFJwAoPqKRibMw3rHAJDhzxkXYPocWX","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAv2kEGINhRX7/Sde/dwSmDWBZMEsu5iAc4cJFPO2BGUvi+t1hQNkr3uUh","AAAAAAAAAAAAAAAAAAAAAAAAAL9pBBiDYUV+/0kAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4KvH8vCqKOf51LNW5xGibAVy5Ksz0QbWrHoDDLbRUR5v"],"proofs":[{"total":256,"index":25,"leaf_hash":"AsSjqjR6t7DoYUApYX3+UzxBWjfemRmDZEqaTmru1vw=","aunts":["wrgVNSfPC7SrdYbSGXTUPNry6ZkXpJKHOX1Gm1V7XKs=","w7aYDssyRcFBgbC9kUW4iAqInDC8n5L+kOrX7Ja4+yI=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":26,"leaf_hash":"hsH77sEx7KiptABg/5ua2sTv99hwzslBCWSW7o25Rys=","aunts":["aGfFvzoQ6JnKTI3uhuzz34dPpRsptmNUEJIc27djAgM=","Zv/+K21F/3UNmzJ0PTPt6GWaF2JCrDKeBNEH7SBh1Jg=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":27,"leaf_hash":"aGfFvzoQ6JnKTI3uhuzz34dPpRsptmNUEJIc27djAgM=","aunts":["hsH77sEx7KiptABg/5ua2sTv99hwzslBCWSW7o25Rys=","Zv/+K21F/3UNmzJ0PTPt6GWaF2JCrDKeBNEH7SBh1Jg=","O4gMZhvNUL9Soqz9UowcyDlOX9i22XVUBq4lHbAgLmU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":28,"leaf_hash":"DCEKoftPvQN1/NIRcgkrDmMfuE17s3ESiBCvuaZKz0Q=","aunts":["4bdIMilhceFya6whT0/Bu6a5KMJZ/Flz9ZIPhcMnr5I=","+m7i6F0vrDq0QIYK6zjposSkJH+BysRyA4Riv/4mPA8=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":29,"leaf_hash":"4bdIMilhceFya6whT0/Bu6a5KMJZ/Flz9ZIPhcMnr5I=","aunts":["DCEKoftPvQN1/NIRcgkrDmMfuE17s3ESiBCvuaZKz0Q=","+m7i6F0vrDq0QIYK6zjposSkJH+BysRyA4Riv/4mPA8=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":30,"leaf_hash":"BJPKbLGHTF+uwRJvks9DtO82Df+CwZeAW8hHbWirK/Q=","aunts":["SCZu6CK6r/Urxzf3BTv4Z23a51W0Rsv9PLRvo7pF+g4=","d+LG2YyYnl0254cVn8A6Z6c3JCkxETILii/aAb06JvA=","uXA8Wbu9BExlKm7mhE9OLtU02q9fOMIYMOkOE5Qx+YU=","geyG6wjAuL/l99MXRG5KXt0qggB2y03bzSL1lyZMpFo=","wZcJRZbcwBtj8KIRrSvwG0sRm4zPOWiihWXMG8lMQAs=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":25,"end_row":30},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw55ftBN3Ljty9QArZiML3RhO++ZmJxVkkstY6DeHUMlFg","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw50FO8zwyKMbw2yR/I6UCeFWgWZVHgWHPObjGXkmM8V+z","/////////////////////////////////////////////////////////////////////////////xF0xgnLfzB1CcMdgxmkI6FgnmbD0C7GxyL8PHqax+78"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////7/bNb2lN6ptRi0FUvkZwGBIxgKUUkqJCXv8KDrKmAfr"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////bHzFhLVO2jnpNPDO4v8WDahQAgu3egIJeKjObPO8eX"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3Cidk+YasK6QpgtJGbVw3Cwkf2LaQlEJ6HDP6W69YZS"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yDkZgsacq57iFXo5npLVtUeKKlNruAtR6v7F9hnPXTe"],"is_max_namespace_ignored":true},{"end":46,"nodes":["//////////////////////////////////////7//////////////////////////////////////lrD0qJ9dspxSO1Yl8NDioZfgOm8Yj63Y+BGDRHlKCRj","//////////////////////////////////////7//////////////////////////////////////i78l7x6hc+r4PDOj/ojJFtChc0fWOiDF42n8pbp/dUO","/////////////////////////////////////////////////////////////////////////////wGsLPfGqZ0/JFhGq5VHMfK/1Ih8Hmea7vGlbthOGkHs"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYKfXNY34XlDHW9SqQE1QwM/GZq/FqUM0rFthRqm6fFXr","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYERP+1uer4FlYctfE6F/ScJPS7Ba8bhB/nSZoyDtoI8t","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYFipUSlHMh67gzo5qQcTax9/oojvw43kmaPeBvPt37lU","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYF2AvT9ihF842TQa7x/Jj+clC25wZ2MJVxgUtGbaPNCi","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYKsCKq636GZHkw/Eu12Anjtw0DWK5fm0pmlUitm6v3kY","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmD//////////////////////////////////////hQzHMaqzEO9Oi3khPuR8qMY5/8WzeZg6CGwCCIeKkKO"],"proofs":[{"total":256,"index":45,"leaf_hash":"DrzO6G5fO5JqCKdiVXPwHYSl2n0vZBGR8gcg1LWpBuQ=","aunts":["r52gHjdGOgYKzvKhztrUQsz7WJ7CVnO6dW/y/2P2Bo0=","bexsUeu3vWCUJPdYKdBwe661vrKtfvOhWdQWp3N9wuA=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":46,"leaf_hash":"rFEBJL/ODi9u+IlEDm2xKz7YsDCGdM/jlR0PRZhBa5c=","aunts":["BxsWwPG3B510BNpIQ8wPJJ0vq+5acGI7WIVNJGy+Pro=","KTF1Y8cgcJlxd93AayWBKqH65l0WELH293VddDZzsKU=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":47,"leaf_hash":"BxsWwPG3B510BNpIQ8wPJJ0vq+5acGI7WIVNJGy+Pro=","aunts":["rFEBJL/ODi9u+IlEDm2xKz7YsDCGdM/jlR0PRZhBa5c=","KTF1Y8cgcJlxd93AayWBKqH65l0WELH293VddDZzsKU=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":48,"leaf_hash":"8shFGJHTC4NpRBdBlFiCUL/YGQmBjjyyDr352zB1pPA=","aunts":["T4i9+pk0u8mIHXxfgqHL964LCtSP3gK19g+K6MVMZBs=","eb8E9eowZS7pyYJxhOt/i5HesQZEOJEhATjUu7bv94o=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","6V7oO88meKCesdOkWaAKUKfSiFUdNWpBUDSfVUfUuLg=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":49,"leaf_hash":"T4i9+pk0u8mIHXxfgqHL964LCtSP3gK19g+K6MVMZBs=","aunts":["8shFGJHTC4NpRBdBlFiCUL/YGQmBjjyyDr352zB1pPA=","eb8E9eowZS7pyYJxhOt/i5HesQZEOJEhATjUu7bv94o=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","6V7oO88meKCesdOkWaAKUKfSiFUdNWpBUDSfVUfUuLg=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":50,"leaf_hash":"PcM6CC4Jd2od5xmSVHVk9nR9Mka3xfbcFPsIvY8doo0=","aunts":["d9fGI1EsGrgEWpF/mcULqgXcZV9XCv2G1rpFOlZVzYc=","VJg4XoWX2N/FPwPueUg58lvHWLiw3neoV0+NxuISZP8=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","6V7oO88meKCesdOkWaAKUKfSiFUdNWpBUDSfVUfUuLg=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":45,"end_row":50},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":40,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw55ftBN3Ljty9QArZiML3RhO++ZmJxVkkstY6DeHUMlFg","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw50FO8zwyKMbw2yR/I6UCeFWgWZVHgWHPObjGXkmM8V+z","/////////////////////////////////////////////////////////////////////////////xF0xgnLfzB1CcMdgxmkI6FgnmbD0C7GxyL8PHqax+78"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////7/bNb2lN6ptRi0FUvkZwGBIxgKUUkqJCXv8KDrKmAfr"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////bHzFhLVO2jnpNPDO4v8WDahQAgu3egIJeKjObPO8eX"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////3Cidk+YasK6QpgtJGbVw3Cwkf2LaQlEJ6HDP6W69YZS"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yDkZgsacq57iFXo5npLVtUeKKlNruAtR6v7F9hnPXTe"],"is_max_namespace_ignored":true},{"end":46,"nodes":["//////////////////////////////////////7//////////////////////////////////////lrD0qJ9dspxSO1Yl8NDioZfgOm8Yj63Y+BGDRHlKCRj","//////////////////////////////////////7//////////////////////////////////////i78l7x6hc+r4PDOj/ojJFtChc0fWOiDF42n8pbp/dUO","/////////////////////////////////////////////////////////////////////////////wGsLPfGqZ0/JFhGq5VHMfK/1Ih8Hmea7vGlbthOGkHs"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYKfXNY34XlDHW9SqQE1QwM/GZq/FqUM0rFthRqm6fFXr","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYERP+1uer4FlYctfE6F/ScJPS7Ba8bhB/nSZoyDtoI8t","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYFipUSlHMh67gzo5qQcTax9/oojvw43kmaPeBvPt37lU","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYF2AvT9ihF842TQa7x/Jj+clC25wZ2MJVxgUtGbaPNCi","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmAAAAAAAAAAAAAAAAAAAAAAAAAA9EPvLV3s8bWqYKsCKq636GZHkw/Eu12Anjtw0DWK5fm0pmlUitm6v3kY","AAAAAAAAAAAAAAAAAAAAAAAAAPRD7y1d7PG1qmD//////////////////////////////////////hQzHMaqzEO9Oi3khPuR8qMY5/8WzeZg6CGwCCIeKkKO"],"proofs":[{"total":256,"index":45,"leaf_hash":"DrzO6G5fO5JqCKdiVXPwHYSl2n0vZBGR8gcg1LWpBuQ=","aunts":["r52gHjdGOgYKzvKhztrUQsz7WJ7CVnO6dW/y/2P2Bo0=","bexsUeu3vWCUJPdYKdBwe661vrKtfvOhWdQWp3N9wuA=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":46,"leaf_hash":"rFEBJL/ODi9u+IlEDm2xKz7YsDCGdM/jlR0PRZhBa5c=","aunts":["BxsWwPG3B510BNpIQ8wPJJ0vq+5acGI7WIVNJGy+Pro=","KTF1Y8cgcJlxd93AayWBKqH65l0WELH293VddDZzsKU=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":47,"leaf_hash":"BxsWwPG3B510BNpIQ8wPJJ0vq+5acGI7WIVNJGy+Pro=","aunts":["rFEBJL/ODi9u+IlEDm2xKz7YsDCGdM/jlR0PRZhBa5c=","KTF1Y8cgcJlxd93AayWBKqH65l0WELH293VddDZzsKU=","m+9bQve1WK9BL1ACTHR3nujhE5zvnb2fXy8PhGpE9W8=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":48,"leaf_hash":"8shFGJHTC4NpRBdBlFiCUL/YGQmBjjyyDr352zB1pPA=","aunts":["T4i9+pk0u8mIHXxfgqHL964LCtSP3gK19g+K6MVMZBs=","eb8E9eowZS7pyYJxhOt/i5HesQZEOJEhATjUu7bv94o=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","6V7oO88meKCesdOkWaAKUKfSiFUdNWpBUDSfVUfUuLg=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":49,"leaf_hash":"T4i9+pk0u8mIHXxfgqHL964LCtSP3gK19g+K6MVMZBs=","aunts":["8shFGJHTC4NpRBdBlFiCUL/YGQmBjjyyDr352zB1pPA=","eb8E9eowZS7pyYJxhOt/i5HesQZEOJEhATjUu7bv94o=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","6V7oO88meKCesdOkWaAKUKfSiFUdNWpBUDSfVUfUuLg=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":50,"leaf_hash":"PcM6CC4Jd2od5xmSVHVk9nR9Mka3xfbcFPsIvY8doo0=","aunts":["d9fGI1EsGrgEWpF/mcULqgXcZV9XCv2G1rpFOlZVzYc=","VJg4XoWX2N/FPwPueUg58lvHWLiw3neoV0+NxuISZP8=","1yTW/Bvqb6pHF7xmwv3z6612z7kxJQYTCWguNjXr5tI=","w7ONgDkPc5+x7+Xjz65ttEEbRUwKm5SNjgnssOjEEh4=","6V7oO88meKCesdOkWaAKUKfSiFUdNWpBUDSfVUfUuLg=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":45,"end_row":50},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////w1zHZctG9GraGLen2adwwsowY2JzFf9DCZKIOxHAVG6"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////z+aO6qFLIsX7LCFFP4UNRPNdxILT2da5b1z71svsFvZ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////qqp+FTOiezlEKVpKUp2Qstv+hALsIzaTLSeRuDn2Ou"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////27wJP3ShlQv0NLR3Fy64bxW260UkgmzZXuDQbE2mkiH"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yIteDODxCfg4T1k/bton+NfM49Nl1MVhnxy9AVwx3JS"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2K9DOOCxSU92Gx+fNm9du/W9sFvMw+iLKmC2I6hRMqIX","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2AmV4EHK1ypMXkZ1BznC9EnmnrrAzNTfUdw3YG32gJ/H","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2EBjUABpqa5AEIYV0ICDgkPThIGs+SBI1W+GTgLyxGtX","/////////////////////////////////////////////////////////////////////////////9pVz8oTpS+7dp3plg5KRT711oMkchWmlgiqEQcfR8fC"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Jg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3JgkpMgJ0H1iB8QDom2/NikVxRzBxEbjm4YtDWHlmRIa5","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Jt2bO59bPDNGenm6vnb12a/XafC6gTquHuyGBLlqSiqk","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Ju5U+SP5HZYNn/Wo0qDzpB5v59pD8Gl35nlQYOZwBgK5","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Jk56/0Am1eeODlJ77cxDYjQWrbqo3Khugh5HJmWBYOBT","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3JhfRs1j7oRTYpszj5KgU0qCxVAVRMjQQWsVNYBP9ZIqZ","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2M66vSQ56Xw0SDsh32fNm2hWSYJi0yYbIhcWihB46O16"],"proofs":[{"total":256,"index":10,"leaf_hash":"wHgM0cAd97sRsZkOf8rlh7LPLE8kagRuf06N5uSglNk=","aunts":["qXRqXABBl1K1UueF64ZKLqy69+f+9wWvCQ86tGV3qxg=","qvkcmIClTQfHIHjUda50QzCKfOYQG7/jmDePwqLn5Qs=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":11,"leaf_hash":"qXRqXABBl1K1UueF64ZKLqy69+f+9wWvCQ86tGV3qxg=","aunts":["wHgM0cAd97sRsZkOf8rlh7LPLE8kagRuf06N5uSglNk=","qvkcmIClTQfHIHjUda50QzCKfOYQG7/jmDePwqLn5Qs=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":12,"leaf_hash":"rWwqThfZ0iyUcIhBTltCWMYdrgmvtyznvYiBLbbFBrE=","aunts":["a9kcOi2oLpwJsrcCjkoFvvymgm8ndVtu3f2h+SZGFXs=","4n9vliSeozZGVd3IuJnYsD2O78zIH4Wu5XPG3WwIARM=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":13,"leaf_hash":"a9kcOi2oLpwJsrcCjkoFvvymgm8ndVtu3f2h+SZGFXs=","aunts":["rWwqThfZ0iyUcIhBTltCWMYdrgmvtyznvYiBLbbFBrE=","4n9vliSeozZGVd3IuJnYsD2O78zIH4Wu5XPG3WwIARM=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":14,"leaf_hash":"0T+TjTyrJ6BwUvrbGn6FxPgMZgAB50RbdGgGiwb7AF8=","aunts":["6Gkv2kuFcKYR2RpQNhVNgIi/bWLp2hbTgla9h8x1qQs=","o9+jJWZ1e5cIVA9aTeoVHFIxwIyqRRUynoXzxHFnZlk=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":15,"leaf_hash":"6Gkv2kuFcKYR2RpQNhVNgIi/bWLp2hbTgla9h8x1qQs=","aunts":["0T+TjTyrJ6BwUvrbGn6FxPgMZgAB50RbdGgGiwb7AF8=","o9+jJWZ1e5cIVA9aTeoVHFIxwIyqRRUynoXzxHFnZlk=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":10,"end_row":15},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////w1zHZctG9GraGLen2adwwsowY2JzFf9DCZKIOxHAVG6"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////z+aO6qFLIsX7LCFFP4UNRPNdxILT2da5b1z71svsFvZ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["//////////////////////////////////////////////////////////////////////////////qqp+FTOiezlEKVpKUp2Qstv+hALsIzaTLSeRuDn2Ou"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////27wJP3ShlQv0NLR3Fy64bxW260UkgmzZXuDQbE2mkiH"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////yIteDODxCfg4T1k/bton+NfM49Nl1MVhnxy9AVwx3JS"],"is_max_namespace_ignored":true},{"end":8,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2K9DOOCxSU92Gx+fNm9du/W9sFvMw+iLKmC2I6hRMqIX","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2AmV4EHK1ypMXkZ1BznC9EnmnrrAzNTfUdw3YG32gJ/H","AAAAAAAAAAAAAAAAAAAAAAAAAHD43VRgUkoi29gAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2EBjUABpqa5AEIYV0ICDgkPThIGs+SBI1W+GTgLyxGtX","/////////////////////////////////////////////////////////////////////////////9pVz8oTpS+7dp3plg5KRT711oMkchWmlgiqEQcfR8fC"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Jg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3JgkpMgJ0H1iB8QDom2/NikVxRzBxEbjm4YtDWHlmRIa5","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Jt2bO59bPDNGenm6vnb12a/XafC6gTquHuyGBLlqSiqk","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Ju5U+SP5HZYNn/Wo0qDzpB5v59pD8Gl35nlQYOZwBgK5","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3Jk56/0Am1eeODlJ77cxDYjQWrbqo3Khugh5HJmWBYOBT","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAbfz4MovHVyp3JhfRs1j7oRTYpszj5KgU0qCxVAVRMjQQWsVNYBP9ZIqZ","AAAAAAAAAAAAAAAAAAAAAAAAAG38+DKLx1cqdyYAAAAAAAAAAAAAAAAAAAAAAAAAcPjdVGBSSiLb2M66vSQ56Xw0SDsh32fNm2hWSYJi0yYbIhcWihB46O16"],"proofs":[{"total":256,"index":10,"leaf_hash":"wHgM0cAd97sRsZkOf8rlh7LPLE8kagRuf06N5uSglNk=","aunts":["qXRqXABBl1K1UueF64ZKLqy69+f+9wWvCQ86tGV3qxg=","qvkcmIClTQfHIHjUda50QzCKfOYQG7/jmDePwqLn5Qs=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":11,"leaf_hash":"qXRqXABBl1K1UueF64ZKLqy69+f+9wWvCQ86tGV3qxg=","aunts":["wHgM0cAd97sRsZkOf8rlh7LPLE8kagRuf06N5uSglNk=","qvkcmIClTQfHIHjUda50QzCKfOYQG7/jmDePwqLn5Qs=","rE/it6FG+bWdan9swaFGAnyj/g/sUQGEo/vDSrvjaQM=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":12,"leaf_hash":"rWwqThfZ0iyUcIhBTltCWMYdrgmvtyznvYiBLbbFBrE=","aunts":["a9kcOi2oLpwJsrcCjkoFvvymgm8ndVtu3f2h+SZGFXs=","4n9vliSeozZGVd3IuJnYsD2O78zIH4Wu5XPG3WwIARM=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":13,"leaf_hash":"a9kcOi2oLpwJsrcCjkoFvvymgm8ndVtu3f2h+SZGFXs=","aunts":["rWwqThfZ0iyUcIhBTltCWMYdrgmvtyznvYiBLbbFBrE=","4n9vliSeozZGVd3IuJnYsD2O78zIH4Wu5XPG3WwIARM=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":14,"leaf_hash":"0T+TjTyrJ6BwUvrbGn6FxPgMZgAB50RbdGgGiwb7AF8=","aunts":["6Gkv2kuFcKYR2RpQNhVNgIi/bWLp2hbTgla9h8x1qQs=","o9+jJWZ1e5cIVA9aTeoVHFIxwIyqRRUynoXzxHFnZlk=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":15,"leaf_hash":"6Gkv2kuFcKYR2RpQNhVNgIi/bWLp2hbTgla9h8x1qQs=","aunts":["0T+TjTyrJ6BwUvrbGn6FxPgMZgAB50RbdGgGiwb7AF8=","o9+jJWZ1e5cIVA9aTeoVHFIxwIyqRRUynoXzxHFnZlk=","mMnfePLjQUAwygg2bvawv7btHMtW8I9zd3kG4yICtYE=","e1MxuyXUr4JZeKWQFvMJna0p0/DxXBVa71JGlMw7oIY=","2PkINHC48udJyPRriJnMlFzlbwDzEpFeKIK7ve3rvqk=","doFG9r4XQ3nCXYW7NUT09bq0oj5T5vB4Tp1pohj7Rj4=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":10,"end_row":15},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":24,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4E70GmtUaAaib7B4dNSmNoCu2QG6qv9ZMUonGNE7Uq5X","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4D3xu+SQCHHBJJwcmBYhOx2YwV8HZAI/op2k4HZz5lwQ","/////////////////////////////////////////////////////////////////////////////+Ta4UfRBz3O4c/OYGnRnHhHyboGVuGb6AuVHekEHuHc"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////w5F/hUdZiHjQl/lj5DUO1yAZp2RAgxsGaj3uXDloIUv"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////ziHsEDd9JjwuEhQFnPqlhcc5SNgJVagXC0prm4sJbUf"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////22cA/7Di6OE9O4MWry5fla0PnH4n26j27hzPEckm2TJ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////5L4NjV5TORRjLQ6Zq00D+slv6UfrlPFHcn/SEtkT3on"],"is_max_namespace_ignored":true},{"end":34,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV9pGZMiewDl61A6+GjV0gFfFsbGIOJmzxh84uMN8tCTu","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV29WHb3clg/yrtc5VbCGryR0K+rxp44ekLPPPzXAlHTP","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw5xG8TRtsLuCbDdRbsyJJPF8IsNy/FtejubhpbYcR/YaX","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw5/M0KcGOzxQLW2fZRamKwGSao8RojKEx7iJW3FvieOyQ","/////////////////////////////////////////////////////////////////////////////19iYOFBgfbCxJ5BIfEBFwEAqCWgWKlNtv72f5GjQbHS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBVw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV5Esq7Q/6+rAZF0NjCTwbkX8PEC63i8EQNmLAV9rvbkS","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV9Vgfo3m/GYeMJnZ4yHJG0R4T0qqyIqRdNM0Zdr76Dgd","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV5NDWwMMjZznh8lm23WK5NkcQcZys43KiQk/+7zRx1XV","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV/EyXTPgLhkdDiNEngvi42X7Sgt/u3s0gT/1hVDgZgOx","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV+oRj1lPlJPnfwnCwyWCcWZuEhm2b2ytUh+B7jUp2pYq","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw56RleFZoLpkVb4EarkZgpIf2PHCRsSqdNOGDXMbCWf+t"],"proofs":[{"total":256,"index":35,"leaf_hash":"oDQUdvrwakVZi17pSEJLsuPv6+d6KDF0PlVgEaX369M=","aunts":["zKADZJzH8HOXFruUmEdx9+A13ST0EmUImkMxZI8hFjg=","6eoLn08K4MfRbMEOm8AOCG7zxFfOofIPsAR678yKl9U=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":36,"leaf_hash":"zgaQCGFcFxQhpIQLdWkti4WdPaFpyS2Id2gFa6GVHNI=","aunts":["H7JVds4HaYhgJfeyG50YQzFIGGL+yV4jwFOUVM9m8Ig=","h4v2bGjEPk58LElCasM2QtHXZyD46HBareQrpG0Stf8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":37,"leaf_hash":"H7JVds4HaYhgJfeyG50YQzFIGGL+yV4jwFOUVM9m8Ig=","aunts":["zgaQCGFcFxQhpIQLdWkti4WdPaFpyS2Id2gFa6GVHNI=","h4v2bGjEPk58LElCasM2QtHXZyD46HBareQrpG0Stf8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":38,"leaf_hash":"K7JjKd68WGuKgdd//MTriJcunntP+Q/ReoJTi9ctTVg=","aunts":["0d/ln2hirmgqLDgohMDcNRayFoxIQ5TgSktMvt1wpug=","nYiIbXPAusudvRyEbBZL1dTyyBX9fhhE9o5jmViast8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":39,"leaf_hash":"0d/ln2hirmgqLDgohMDcNRayFoxIQ5TgSktMvt1wpug=","aunts":["K7JjKd68WGuKgdd//MTriJcunntP+Q/ReoJTi9ctTVg=","nYiIbXPAusudvRyEbBZL1dTyyBX9fhhE9o5jmViast8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":40,"leaf_hash":"BeymHfpJwuN1DEltW7EQYZ2Cwb5YmhzPi+Z8SlnwmPA=","aunts":["puC/o1wmeYSbUK8SKljanN+JYKEffGlvZ1vnG0hpLwI=","/hH0Tf0JU91CiqsP3qWZAPRTOs42ha9bkyi1+AJGIvo=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":35,"end_row":40},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":24,"end":64,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4E70GmtUaAaib7B4dNSmNoCu2QG6qv9ZMUonGNE7Uq5X","AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAAy7rZ9NXtgBU/4D3xu+SQCHHBJJwcmBYhOx2YwV8HZAI/op2k4HZz5lwQ","/////////////////////////////////////////////////////////////////////////////+Ta4UfRBz3O4c/OYGnRnHhHyboGVuGb6AuVHekEHuHc"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////w5F/hUdZiHjQl/lj5DUO1yAZp2RAgxsGaj3uXDloIUv"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////ziHsEDd9JjwuEhQFnPqlhcc5SNgJVagXC0prm4sJbUf"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////22cA/7Di6OE9O4MWry5fla0PnH4n26j27hzPEckm2TJ"],"is_max_namespace_ignored":true},{"end":64,"nodes":["/////////////////////////////////////////////////////////////////////////////5L4NjV5TORRjLQ6Zq00D+slv6UfrlPFHcn/SEtkT3on"],"is_max_namespace_ignored":true},{"end":34,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV9pGZMiewDl61A6+GjV0gFfFsbGIOJmzxh84uMN8tCTu","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV29WHb3clg/yrtc5VbCGryR0K+rxp44ekLPPPzXAlHTP","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw5xG8TRtsLuCbDdRbsyJJPF8IsNy/FtejubhpbYcR/YaX","AAAAAAAAAAAAAAAAAAAAAAAAAPEvo8mXfnSrsOcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw5/M0KcGOzxQLW2fZRamKwGSao8RojKEx7iJW3FvieOyQ","/////////////////////////////////////////////////////////////////////////////19iYOFBgfbCxJ5BIfEBFwEAqCWgWKlNtv72f5GjQbHS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBVw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAMu62fTV7YAVP+AAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV5Esq7Q/6+rAZF0NjCTwbkX8PEC63i8EQNmLAV9rvbkS","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV9Vgfo3m/GYeMJnZ4yHJG0R4T0qqyIqRdNM0Zdr76Dgd","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV5NDWwMMjZznh8lm23WK5NkcQcZys43KiQk/+7zRx1XV","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV/EyXTPgLhkdDiNEngvi42X7Sgt/u3s0gT/1hVDgZgOx","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA2N8v5s5a3PYBV+oRj1lPlJPnfwnCwyWCcWZuEhm2b2ytUh+B7jUp2pYq","AAAAAAAAAAAAAAAAAAAAAAAAANjfL+bOWtz2AVcAAAAAAAAAAAAAAAAAAAAAAAAA8S+jyZd+dKuw56RleFZoLpkVb4EarkZgpIf2PHCRsSqdNOGDXMbCWf+t"],"proofs":[{"total":256,"index":35,"leaf_hash":"oDQUdvrwakVZi17pSEJLsuPv6+d6KDF0PlVgEaX369M=","aunts":["zKADZJzH8HOXFruUmEdx9+A13ST0EmUImkMxZI8hFjg=","6eoLn08K4MfRbMEOm8AOCG7zxFfOofIPsAR678yKl9U=","grGsyOfR4InPvpOaDjdzl58FHgdBxf/PbFE49ZOlIHw=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":36,"leaf_hash":"zgaQCGFcFxQhpIQLdWkti4WdPaFpyS2Id2gFa6GVHNI=","aunts":["H7JVds4HaYhgJfeyG50YQzFIGGL+yV4jwFOUVM9m8Ig=","h4v2bGjEPk58LElCasM2QtHXZyD46HBareQrpG0Stf8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":37,"leaf_hash":"H7JVds4HaYhgJfeyG50YQzFIGGL+yV4jwFOUVM9m8Ig=","aunts":["zgaQCGFcFxQhpIQLdWkti4WdPaFpyS2Id2gFa6GVHNI=","h4v2bGjEPk58LElCasM2QtHXZyD46HBareQrpG0Stf8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":38,"leaf_hash":"K7JjKd68WGuKgdd//MTriJcunntP+Q/ReoJTi9ctTVg=","aunts":["0d/ln2hirmgqLDgohMDcNRayFoxIQ5TgSktMvt1wpug=","nYiIbXPAusudvRyEbBZL1dTyyBX9fhhE9o5jmViast8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":39,"leaf_hash":"0d/ln2hirmgqLDgohMDcNRayFoxIQ5TgSktMvt1wpug=","aunts":["K7JjKd68WGuKgdd//MTriJcunntP+Q/ReoJTi9ctTVg=","nYiIbXPAusudvRyEbBZL1dTyyBX9fhhE9o5jmViast8=","0QpzL5P5ue1hpvtV2SAWvl/pVWqZ64rQ3O37ifcmVMs=","rhuIgamjlNaIrd5OK+Wy2q4nChqHAqUOquRuEcj3Ax0=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]},{"total":256,"index":40,"leaf_hash":"BeymHfpJwuN1DEltW7EQYZ2Cwb5YmhzPi+Z8SlnwmPA=","aunts":["puC/o1wmeYSbUK8SKljanN+JYKEffGlvZ1vnG0hpLwI=","/hH0Tf0JU91CiqsP3qWZAPRTOs42ha9bkyi1+AJGIvo=","JIxd8WMyqwmVtt2lLxPhuh3Cssrx4SJcHVzRmoJKPlc=","QWZzS9IsazSsmfgxOPmf+PKcsC6i/fwPsUvPW7zn7GA=","vOBISObLBsg9RZynNy1LlnRANLok61pfYLHNu7H1QZU=","GQBkdeGqTvN/Ay0eTEt3P4f1x26kFYRI25MX1faK3EI=","Zr7nZJ7FOl8R6XRh+Kzl1fuwKMFeS4cHJf6oNSQ65jQ=","sTvGja0yEQaqqn/ooZr5UojqA++eRnoVTdp58b0jza4="]}],"start_row":35,"end_row":40},"namespace_version":0},"root":"l+KxXkcUTBBUrNE3sZfcmG/1x+lTfwLa9OM8yLW9p7A=","sub_threshold":64}] \ No newline at end of file diff --git a/blob/testdata/fuzz-corpus/verify-76657279206c6172676520626c6f6273207e3135303020736861726573.json b/blob/testdata/fuzz-corpus/verify-76657279206c6172676520626c6f6273207e3135303020736861726573.json new file mode 100755 index 0000000000..7b52dc9f7c --- /dev/null +++ b/blob/testdata/fuzz-corpus/verify-76657279206c6172676520626c6f6273207e3135303020736861726573.json @@ -0,0 +1 @@ +[{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6rP6BGmWqhRjsgKEohT4J1aevH2NcAeYIGnkmlNewr7I","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6oNlc4xAiuYc1ktn+ekleAHUVj7MfrSLXw53fWvWCCGv","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6rGagjacz2eLNRCQ0e5jjIfpTocUtLDeLhlJNhwfNUz4","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6vsO38G4gOmujFhgUr2vutR9umJ/MDG/ZQh6ui4w5GxT","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6o859SboBaZ29anMIDFDZmX3Wc3zRRnLrNuvAmMp/R+l","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6pSOvQ8o4/FO10ai6+FSY27TVQDfTzfsRnzxLOJlRbfH","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6kRAcxXOMOZLPLyEiKH3z1XoeRUbMx71ejyLsoDXGSoR","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6vBDJP4/OSHksvHsPSeAc5JjNnGLeCgKnjXtokONz6gF","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6qe9kz4I6TfllNy1x4/WWe7X4GeE92jgWRMU41ujF+RJ","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6gYQFD3b/UTbEX5+7pDItbumOxnpRKE3ETInOTKQyS6E","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6gsl9O8GIWhs9UgB38KnELqwlkaw+n7cGKyQF4Siemij","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6iVWx/EprUv1HNUpAJJyZ2gztN6tLWcAWxssFmaPC1ah","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6olUFms26u2bUy1ehsnn2+twHvzFgIfj+nM144ChuJuo","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6nSjbCca08+MQKIlaQjmxd2aogIrQtQq2jhhLROuJZFp","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6rwsIdUA6ZtZ2sBKiEX6HyNCGN4OCoASeMNGur6fm3ZK","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6l/DirSssqclrQtV8NePJG7gCShbkvE2KBM7NiNUZm8o","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6nVqS9w7drSIeAInGnSTjq2Z5WCifzomKb+NDWFJIqMt","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6ksHTMFDw7NTjjCYP4NSeMfXXC0zEshOnt7zc+Ye8h6M","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6p7kvdFDWCrI0MUcMeZTq/HiR4FqRgCH3akCe6j575DU","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6g3sb+YXDX4U2B4DZyNCFgpf/7jzSn1T2eeE6CSfuKZs","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6iosAv3zuhHpMMekxV+eBbT2tenNEnWDiTgI9/WPo8Oc","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6jOgqRGJcMihCeEDXSETKHghXqulG7r26RFZYlGV033p","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6l6WXggykT79BS7h6rdgnSyLBEBpFrSeVBwR4sxXDyMV","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6hWEqypo0ec4ogQzZnHJ3ZcE4h3wv0zkeXm2YcQR09E7","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6vNxq4IG54hXot6WJidNf9OUYPhCUlvGs/7fGLOWx3iq","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6uY24LV5zc5Jg1qcV8/GlsaEKKdEtFWC+zSwI2sl4Du5","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6stNj03T3hKGksjTu+CHQtTXvmkZ6AEh/h2QfewlTDB2","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6nb6dpSLbUsZs+d3W4srNStBjzcqyN2mN0yjRNKUiOnD","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6mTghTkqWsE971FKKFR5JGPNFjT2I4KveMHlGDcjLXec","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6rGJ0UCtz+fBVNjPPyoneMoGDbajpYuA/ooa0UEbTubL","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6rp/zfsCFNXjIkV4d1XAmXRh6EsdTw96wd0xVKwXGVin","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6lmgubWugJ64Yg3oU26GhRtMjdG0MVXnGxXsZWFpyDWn","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6uGeBNrYbAOpB/0ux53GTn881B9y4/YT0jM/q36+BfA0","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6gDOlzaTs84XAAlGF0YRdzrjtKyz7lQBnE3HhpLHQVGT","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6ryQ7flppGgCrgJ4yZc/z+Bz3nn545WMdoxh1cSW/gb9","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6vZhYvZaAJ8UUcw93fvkz5AUyZGciQkRd/8LvQeTkFld","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6p2YwylwTXtGWZAa7mM6nKNSQCNg5RxTPAnUWl25SZG7","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6sQyRYFLpLg8CQraXRUCHgsJ87xak7vSvuy94bL3vJjF","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6mmyCVacNazYgxyg98V7xO1FHcKLU0ztwMkwJUSZCXvt","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6s/aWT6NqulY95pbW86frxqdjGEBBR7SoEFvTLLNRDgk","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6kC3rmaZp+2Ek1ajhHimBKmGDUD5BNHahkeTwuuxuVrV","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6kLOKcT54Q2oyxUNIZ5rgQNNzGR9yGHHdhvysCAEyWj7","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6hyHiXxZK1EWiGVj3w5mHkS1swkqyPfNKC/u0cYH5hjo","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6gz/jQIONXoP09vDoSM6s5bMuSkeIMsXmkbr7WW/bQk5","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6lRusUNb53QJcsFH4SM/Mff9tKvlDy5wDibDuQOyYroJ","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6ktdGSSehFRXaCHbknHgJk31l9Kx57p6WEuWqj5lZyA5","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6vppERUPXBY7JOb4eCRm0LZm774LVQ94rYXC/HvI56+O","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6tgpiPiXYsgBM6mvXGern8/hX522DO5rGO7CI3JQxxSN","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6kadfik7AryUTZrKsYYOqwTbyvI8wrUwYHjms7F1hvgx","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6mLGlT3KTrW7OcQoueBAtef5dElklCZ4Q2K/OXq3sav5","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6ogykxku7kCIpqtr9qFIXKfKsQfJkwc+kFVxbQC2X6h4"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizmvqQsm9GBhUJXqNDCWLDfp9Ey1LJGu0LjsI9iem7e4S","/////////////////////////////////////////////////////////////////////////////wpN0WsQhEbamwxNSF3v+5QmVOOeqsBX5GH90ON44Ggg"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zDqGpnpFpnk09e4jbnXohe6lsNmjbJb0eA+13bm5dJm"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3GA80pUADVB93jgjd40fp+Qg2ZD3oUT4Op94ak7pnf/"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xtyxcz0cfoJaCViyW+I4VcMP8bAGkNsk1lfFoWqE+9d"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7eq08kaThr3grUsUdIb8FvHAtUNPRvGswfVyRyYnxKM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wJov4mv4D84IFiOb77M14joOihZVB5mHlKaCWbTlVKb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+aYIOvIor+Kv2++cogQzAxvYoC8MyYy1JtnGVpun/5p"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9UW+jtTCGzUJml4cuZRIt+yVOaQUPoXikVayDenQS87"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9NNDULc5AvcVbX5o0SSN9H5k8m/vBUHCWpVtZxiQ7P2"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6GBUK3/AwvSvjgL0a0w2lmNaFiQxinKTjhRa5a7latn"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////636D8N0oTaUHXBGQhXxuJfgDrnH4dBje5svJzjPdG9M"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0L32jmIkYVnjd3P9+jRSWtSMiKyM9BTlRXRh8/JHAVg"],"is_max_namespace_ignored":true},{"end":53,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6uvqMCyO2qNzI3mSnQV8E+nnYJ8I41QAjAkia1M5fNJd","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6h1T83SJ92WohLxrpaxW8Tk/q4i6S+h/tFIpRMgdwpGb","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6uBtUgxIgixdOwoH2Sb0YeBleBPeTub8Pm2nvJ+LmY/K","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Zj6SRkI54VUVE8bQalAxRMc1rCTZczx4elaXn6vb0/q","/////////////////////////////////////////////////////////////////////////////7W2wwt0antw0dtJtnHC8vEnktrJYsnV/7YZglI9LvrA"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6lw7mcgxgJmgYlYsdf2f0jVDWoEgP9/5tuFHDjDISY0v","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6ioU304WJBThzOmfr3ALlRlrGtBbxgz89X/pQxKVJNH6","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6nHm7eISENrUPGszB2EJ89kO/4TahGyTS8S66/uUbudn","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6sPTu9RmyGOvFwNEYugXoDECVJxkzjuRw+cnaFzXH1vI","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6sTRIi9unMgI3zMEk87YcCizCytPhBK1QqaZkRNdM8rw","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6pqHaP/RW+ZXZHspf7U5zje0RukXSi0evJCdmXU0fJbj","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6hlIfMfPU2COHjIbXknpMZK0+89gZzjDl4Ruiwa1SGKV","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6jcYgd2rso/PJXX67t5UFgpwXeEfQP6/KdVgMpAdtruq","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6pYpEhHMMkAzkXOKy+gNR/gfR441Yk7EZdQM2Pc0jaBG","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6s4xwAZuZeGbKJqanL6UhvwwCrB1aYxRA71r+V3yYEr7","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6jvRhqGtms4puubDEu0uGcrCCgg+/Te65jPyQBcWGbWg","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6runOMDMJM7NtXXmsmwylg/QSbeYufZUc6aOfxXgIejv","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8YaEbuXGDZTujjQQX3aV4yGqymV9XJewv7Pd7x02hWX2"],"proofs":[{"total":512,"index":37,"leaf_hash":"H6Bsm1vo5LPYh6Cw3OKquRbXrAbMUEd7J4MK6Q/uBKk=","aunts":["uAg7ghc128txB+MPmtveLuOYIF1IWiWOPsFBHuJ2PE4=","3I20LGLactE5JAKpAcEnrJHltyDE2zbGv6bdXu2KSe8=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":38,"leaf_hash":"OlMiG9RHUICe2chM7odNqpOtNMZQSLDdWswnXB56OnY=","aunts":["1vIQKOvJ+QVgZZjJ7upuS6ISMNIV+Z9u9mQLVooka0M=","e6TTh1XCwuAPXyQCMAPbybX3Fp5B9EiRTlJZihcbeUA=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":39,"leaf_hash":"1vIQKOvJ+QVgZZjJ7upuS6ISMNIV+Z9u9mQLVooka0M=","aunts":["OlMiG9RHUICe2chM7odNqpOtNMZQSLDdWswnXB56OnY=","e6TTh1XCwuAPXyQCMAPbybX3Fp5B9EiRTlJZihcbeUA=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":40,"leaf_hash":"dqNYvJpV8yssF+Ujm4KeQ/ob8/poAs8VqNZjUeY//jE=","aunts":["ijVLzBO8e85e/yn3bJMgED3E9EH5X8HV2dlKm8FW+ok=","+fBvWg9fkUJB6TcMBmIqxliOuzPOAzIVWawAunRUsZg=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":41,"leaf_hash":"ijVLzBO8e85e/yn3bJMgED3E9EH5X8HV2dlKm8FW+ok=","aunts":["dqNYvJpV8yssF+Ujm4KeQ/ob8/poAs8VqNZjUeY//jE=","+fBvWg9fkUJB6TcMBmIqxliOuzPOAzIVWawAunRUsZg=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":42,"leaf_hash":"mUsnGVYpqTUH5aPkxrDIsLReJPO6E9FmuSf5eH8dtw4=","aunts":["rwz/WktwjLgesDSpikr3W2oYOFs7RDqDMlJKMsp3j6I=","xAwgL8K2AGfREX4rX6di8fV2sn19xBdXdELiTn5fPLc=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":43,"leaf_hash":"rwz/WktwjLgesDSpikr3W2oYOFs7RDqDMlJKMsp3j6I=","aunts":["mUsnGVYpqTUH5aPkxrDIsLReJPO6E9FmuSf5eH8dtw4=","xAwgL8K2AGfREX4rX6di8fV2sn19xBdXdELiTn5fPLc=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":44,"leaf_hash":"bgaFPA5tu5F5+Ex+5i8dWuYQTQUw/NzJJBpqb5DGv7E=","aunts":["NKW7ifrANGXV4rUeuY9lRJlRcUL2yqWBnAF9hggocn4=","asDuz9YrJBBptiLMbbusugcNr7jmqcfl8BCjF8XtXE8=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":45,"leaf_hash":"NKW7ifrANGXV4rUeuY9lRJlRcUL2yqWBnAF9hggocn4=","aunts":["bgaFPA5tu5F5+Ex+5i8dWuYQTQUw/NzJJBpqb5DGv7E=","asDuz9YrJBBptiLMbbusugcNr7jmqcfl8BCjF8XtXE8=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":46,"leaf_hash":"UCnFqdAQ+FAPRASmdsYHDTkG78YdNaHkNR1/SjpYqQk=","aunts":["zH60uiUNh26TwtuFGe5pKEGZnsdBDmqZGJnsMx5PL7k=","33Xyylrlwx2zx6tSrBjaCEVbTgIknZXZ40ATdinqx3c=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":47,"leaf_hash":"zH60uiUNh26TwtuFGe5pKEGZnsdBDmqZGJnsMx5PL7k=","aunts":["UCnFqdAQ+FAPRASmdsYHDTkG78YdNaHkNR1/SjpYqQk=","33Xyylrlwx2zx6tSrBjaCEVbTgIknZXZ40ATdinqx3c=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":48,"leaf_hash":"3+kuOxj/rci7mdVYxRQcbQCxy9s8E8JADvbQSKH7zO0=","aunts":["t+6q2X4zPYpLi9PlikBy2tw8fmh/plafNz1YUl1VRlc=","B85OEuK9iTUwdHrH7BqUX5h0SDfqSyVxb63Y8qu+m68=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":49,"leaf_hash":"t+6q2X4zPYpLi9PlikBy2tw8fmh/plafNz1YUl1VRlc=","aunts":["3+kuOxj/rci7mdVYxRQcbQCxy9s8E8JADvbQSKH7zO0=","B85OEuK9iTUwdHrH7BqUX5h0SDfqSyVxb63Y8qu+m68=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":37,"end_row":49},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizmvqQsm9GBhUJXqNDCWLDfp9Ey1LJGu0LjsI9iem7e4S","/////////////////////////////////////////////////////////////////////////////wpN0WsQhEbamwxNSF3v+5QmVOOeqsBX5GH90ON44Ggg"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zDqGpnpFpnk09e4jbnXohe6lsNmjbJb0eA+13bm5dJm"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3GA80pUADVB93jgjd40fp+Qg2ZD3oUT4Op94ak7pnf/"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xtyxcz0cfoJaCViyW+I4VcMP8bAGkNsk1lfFoWqE+9d"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7eq08kaThr3grUsUdIb8FvHAtUNPRvGswfVyRyYnxKM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wJov4mv4D84IFiOb77M14joOihZVB5mHlKaCWbTlVKb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+aYIOvIor+Kv2++cogQzAxvYoC8MyYy1JtnGVpun/5p"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9UW+jtTCGzUJml4cuZRIt+yVOaQUPoXikVayDenQS87"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9NNDULc5AvcVbX5o0SSN9H5k8m/vBUHCWpVtZxiQ7P2"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6GBUK3/AwvSvjgL0a0w2lmNaFiQxinKTjhRa5a7latn"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////636D8N0oTaUHXBGQhXxuJfgDrnH4dBje5svJzjPdG9M"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0L32jmIkYVnjd3P9+jRSWtSMiKyM9BTlRXRh8/JHAVg"],"is_max_namespace_ignored":true},{"end":53,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6uvqMCyO2qNzI3mSnQV8E+nnYJ8I41QAjAkia1M5fNJd","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6h1T83SJ92WohLxrpaxW8Tk/q4i6S+h/tFIpRMgdwpGb","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6uBtUgxIgixdOwoH2Sb0YeBleBPeTub8Pm2nvJ+LmY/K","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Zj6SRkI54VUVE8bQalAxRMc1rCTZczx4elaXn6vb0/q","/////////////////////////////////////////////////////////////////////////////7W2wwt0antw0dtJtnHC8vEnktrJYsnV/7YZglI9LvrA"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6lw7mcgxgJmgYlYsdf2f0jVDWoEgP9/5tuFHDjDISY0v","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6ioU304WJBThzOmfr3ALlRlrGtBbxgz89X/pQxKVJNH6","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6nHm7eISENrUPGszB2EJ89kO/4TahGyTS8S66/uUbudn","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6sPTu9RmyGOvFwNEYugXoDECVJxkzjuRw+cnaFzXH1vI","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6sTRIi9unMgI3zMEk87YcCizCytPhBK1QqaZkRNdM8rw","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6pqHaP/RW+ZXZHspf7U5zje0RukXSi0evJCdmXU0fJbj","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6hlIfMfPU2COHjIbXknpMZK0+89gZzjDl4Ruiwa1SGKV","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6jcYgd2rso/PJXX67t5UFgpwXeEfQP6/KdVgMpAdtruq","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6pYpEhHMMkAzkXOKy+gNR/gfR441Yk7EZdQM2Pc0jaBG","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6s4xwAZuZeGbKJqanL6UhvwwCrB1aYxRA71r+V3yYEr7","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6jvRhqGtms4puubDEu0uGcrCCgg+/Te65jPyQBcWGbWg","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6runOMDMJM7NtXXmsmwylg/QSbeYufZUc6aOfxXgIejv","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8YaEbuXGDZTujjQQX3aV4yGqymV9XJewv7Pd7x02hWX2"],"proofs":[{"total":512,"index":37,"leaf_hash":"H6Bsm1vo5LPYh6Cw3OKquRbXrAbMUEd7J4MK6Q/uBKk=","aunts":["uAg7ghc128txB+MPmtveLuOYIF1IWiWOPsFBHuJ2PE4=","3I20LGLactE5JAKpAcEnrJHltyDE2zbGv6bdXu2KSe8=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":38,"leaf_hash":"OlMiG9RHUICe2chM7odNqpOtNMZQSLDdWswnXB56OnY=","aunts":["1vIQKOvJ+QVgZZjJ7upuS6ISMNIV+Z9u9mQLVooka0M=","e6TTh1XCwuAPXyQCMAPbybX3Fp5B9EiRTlJZihcbeUA=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":39,"leaf_hash":"1vIQKOvJ+QVgZZjJ7upuS6ISMNIV+Z9u9mQLVooka0M=","aunts":["OlMiG9RHUICe2chM7odNqpOtNMZQSLDdWswnXB56OnY=","e6TTh1XCwuAPXyQCMAPbybX3Fp5B9EiRTlJZihcbeUA=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":40,"leaf_hash":"dqNYvJpV8yssF+Ujm4KeQ/ob8/poAs8VqNZjUeY//jE=","aunts":["ijVLzBO8e85e/yn3bJMgED3E9EH5X8HV2dlKm8FW+ok=","+fBvWg9fkUJB6TcMBmIqxliOuzPOAzIVWawAunRUsZg=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":41,"leaf_hash":"ijVLzBO8e85e/yn3bJMgED3E9EH5X8HV2dlKm8FW+ok=","aunts":["dqNYvJpV8yssF+Ujm4KeQ/ob8/poAs8VqNZjUeY//jE=","+fBvWg9fkUJB6TcMBmIqxliOuzPOAzIVWawAunRUsZg=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":42,"leaf_hash":"mUsnGVYpqTUH5aPkxrDIsLReJPO6E9FmuSf5eH8dtw4=","aunts":["rwz/WktwjLgesDSpikr3W2oYOFs7RDqDMlJKMsp3j6I=","xAwgL8K2AGfREX4rX6di8fV2sn19xBdXdELiTn5fPLc=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":43,"leaf_hash":"rwz/WktwjLgesDSpikr3W2oYOFs7RDqDMlJKMsp3j6I=","aunts":["mUsnGVYpqTUH5aPkxrDIsLReJPO6E9FmuSf5eH8dtw4=","xAwgL8K2AGfREX4rX6di8fV2sn19xBdXdELiTn5fPLc=","DcMrCnxUWK5dNhEr/fKWZM64B18ayTvwBvz/JR51kJs=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":44,"leaf_hash":"bgaFPA5tu5F5+Ex+5i8dWuYQTQUw/NzJJBpqb5DGv7E=","aunts":["NKW7ifrANGXV4rUeuY9lRJlRcUL2yqWBnAF9hggocn4=","asDuz9YrJBBptiLMbbusugcNr7jmqcfl8BCjF8XtXE8=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":45,"leaf_hash":"NKW7ifrANGXV4rUeuY9lRJlRcUL2yqWBnAF9hggocn4=","aunts":["bgaFPA5tu5F5+Ex+5i8dWuYQTQUw/NzJJBpqb5DGv7E=","asDuz9YrJBBptiLMbbusugcNr7jmqcfl8BCjF8XtXE8=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":46,"leaf_hash":"UCnFqdAQ+FAPRASmdsYHDTkG78YdNaHkNR1/SjpYqQk=","aunts":["zH60uiUNh26TwtuFGe5pKEGZnsdBDmqZGJnsMx5PL7k=","33Xyylrlwx2zx6tSrBjaCEVbTgIknZXZ40ATdinqx3c=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":47,"leaf_hash":"zH60uiUNh26TwtuFGe5pKEGZnsdBDmqZGJnsMx5PL7k=","aunts":["UCnFqdAQ+FAPRASmdsYHDTkG78YdNaHkNR1/SjpYqQk=","33Xyylrlwx2zx6tSrBjaCEVbTgIknZXZ40ATdinqx3c=","cSl/+FO+UdRE/xdJ8WBK7WPKv95dbEHSe5HaI2YtASU=","PtIuY9vzI5MbbOI1ecl2plLy3JVSDF+lW/EujZUSqbk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":48,"leaf_hash":"3+kuOxj/rci7mdVYxRQcbQCxy9s8E8JADvbQSKH7zO0=","aunts":["t+6q2X4zPYpLi9PlikBy2tw8fmh/plafNz1YUl1VRlc=","B85OEuK9iTUwdHrH7BqUX5h0SDfqSyVxb63Y8qu+m68=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":49,"leaf_hash":"t+6q2X4zPYpLi9PlikBy2tw8fmh/plafNz1YUl1VRlc=","aunts":["3+kuOxj/rci7mdVYxRQcbQCxy9s8E8JADvbQSKH7zO0=","B85OEuK9iTUwdHrH7BqUX5h0SDfqSyVxb63Y8qu+m68=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":37,"end_row":49},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":64,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6jRKvBlCipzrEJHJusWUCdjlOvLhWNTTgD+9jP8RcUBr","/////////////////////////////////////////////////////////////////////////////7W2wwt0antw0dtJtnHC8vEnktrJYsnV/7YZglI9LvrA"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zH4Pg6O2drFgRZfeFmXqaRv/GDbfwUZQETZ3AfJpEli"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0iIKEP4vHI6TbHgZf8hzrUKT8W3mzB/QCxGZJq7/mbK"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7m411OQ2Y6xspi58QXwRzLPksTsYgpX37iMnzYq654s"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////7kQMVooMbJfBro44mEBDRq8ZpA8cMTG8IxMYCHwdap"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7Qrrdo/LdmxpAO5gBT7aZFPsYiz8RetGruKpIwbtWln"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yiUqxl0g8BBQbJE+2CuRaukkANKrbDfhjhzTJj3/aZh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7m+YYDESiYYkIA3LUZ4VHGgN1/pGPZuWGgKqv+uLNAy"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6yvtIs6Dh2/HdjJ7mkGab3YLq+TTHGseiaPbkoyMxoY"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wRknGsAf2bPKIhjIpuRrTYIQUFB5RebxeFUSm6JKJqy"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0FbSegOfEh/5/Bz36jBUmWkRTw6EQD17WmoPen6VtBh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6SiFnZoG8V5RSTNbDs/aXWeizmVRWnKfAkT/NvLchqN"],"is_max_namespace_ignored":true},{"end":87,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8X6F62rq6M3SKkzRZCHaEf6TowCG2aooWbMQf42ITjFz","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8SEozuA5BCyPqEOYrMeZTXzD/q2dz+c2MfGo5JjY4tSC","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMpwg/ozjVJAWXR/5/GWIdp3g+C4NIbePOSZOsvIevSwT","/////////////////////////////////////////////////////////////////////////////7KhpG1So7lDEnZoqOkA/wq+/SMAvIKKx1MqoSgx37AA"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Q==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8YaEbuXGDZTujjQQX3aV4yGqymV9XJewv7Pd7x02hWX2","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8WW8QqJCUUJJ3ZS08SB8mjMQH3spjBubmCxDeLdDH3Ii","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Vca17euaoWQKYwWPyixUUSKA0xAH4U6BQDrg0sK5JYc","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8dqKbuGBPwmctQHj7xBqCCU+zC3IUsbMlU7fZ6h2kMqo","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8VWNjVtehk0Wsu2jqKEiudzQgFKjQ1zewFF1NPcUFKeq","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8ZY+SkAg+mhdYf8HUkDk67D7RzXUdhVXwBCy4b+rcy35","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Yha7aW0DJmjoxGx3i+nw07RRbf2MRXU6k9iBE9smfBu","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8dTWdpeYipBjT/Eg5KjHN0t3epjBRo9GycoAPjP/huDC","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8S/fOHIeE/4u8ZLAqOCXaROTb2IAQ6FFtCFmPvu5Sx+p","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8WdEl4OWI801OKkRqzVGjRBkq5oNKgoBcrQR4nV0Quzs","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8a21/5dwG+1BotOM1PwV2icTj1cjl3vnsKH4DgUuTUEX","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8VW9xGNetViGq7jD5xZhEEs4oP+bMGClM+rmbM91q0yr","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMsfjZqMT/6wH0zNNT5dm+1n9WcvwrsvkQBbQci7AEdrN"],"proofs":[{"total":512,"index":49,"leaf_hash":"t+6q2X4zPYpLi9PlikBy2tw8fmh/plafNz1YUl1VRlc=","aunts":["3+kuOxj/rci7mdVYxRQcbQCxy9s8E8JADvbQSKH7zO0=","B85OEuK9iTUwdHrH7BqUX5h0SDfqSyVxb63Y8qu+m68=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":50,"leaf_hash":"EMxY1XChmYRd2CVEtkRXcY7F7e+y1hxVIZ9rIw+WUEI=","aunts":["2IpDbMiOzUNy2YUuhCWbhJN3k+a0BydDhmZgYRiEbUI=","mAyGMMWSUeN+WGU7rbQIfxtjdg1+n4mprESmIUZl5R0=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":51,"leaf_hash":"2IpDbMiOzUNy2YUuhCWbhJN3k+a0BydDhmZgYRiEbUI=","aunts":["EMxY1XChmYRd2CVEtkRXcY7F7e+y1hxVIZ9rIw+WUEI=","mAyGMMWSUeN+WGU7rbQIfxtjdg1+n4mprESmIUZl5R0=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":52,"leaf_hash":"alWjUbyVBY2HJ/cVKmghcxoUMxtspp5joCRUsq0tAGA=","aunts":["dgYVKx7P/yCOYuoicKXGOoPGV72RH9cx4sb6XUJg+jc=","PgVPO8ScLNn/DzOzZkJvxbjBM8gTLy8HqlP3o1HUgaA=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":53,"leaf_hash":"dgYVKx7P/yCOYuoicKXGOoPGV72RH9cx4sb6XUJg+jc=","aunts":["alWjUbyVBY2HJ/cVKmghcxoUMxtspp5joCRUsq0tAGA=","PgVPO8ScLNn/DzOzZkJvxbjBM8gTLy8HqlP3o1HUgaA=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":54,"leaf_hash":"OYJliVYLSC+EKwtuy+jEvNMDGtAYoM3ohf0NL28EOAk=","aunts":["Wcg/AvpR78uk0U1iwUtxja5M64faClNHHaV2+7lxLMQ=","uv/uvrLuT/JWUct4DL6U9imNc5LMCuzWanxhtrjaPm0=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":55,"leaf_hash":"Wcg/AvpR78uk0U1iwUtxja5M64faClNHHaV2+7lxLMQ=","aunts":["OYJliVYLSC+EKwtuy+jEvNMDGtAYoM3ohf0NL28EOAk=","uv/uvrLuT/JWUct4DL6U9imNc5LMCuzWanxhtrjaPm0=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":56,"leaf_hash":"J3fXscTvhou0CEI2fbQOCKFCMmbgziumXM3D/aw9614=","aunts":["jKSB1Ky+NOeu8nuMxUWKOHrhIsDLeBMOfoTDeb4QjFA=","RSfYNoiH+ZdOV7SPFG+adCg3CtVy6J7fbjriqRz20fs=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":57,"leaf_hash":"jKSB1Ky+NOeu8nuMxUWKOHrhIsDLeBMOfoTDeb4QjFA=","aunts":["J3fXscTvhou0CEI2fbQOCKFCMmbgziumXM3D/aw9614=","RSfYNoiH+ZdOV7SPFG+adCg3CtVy6J7fbjriqRz20fs=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":58,"leaf_hash":"wJ+GksGvM8TP6bX9hThZ5hALCrx0jeahl+eyZRMubFg=","aunts":["Mwcg998v+V8g6UoBm8z6/eC3kxuIQhpLDoltmpmcsNI=","Ti5WWO38ZsPvLgOKVV4EiEcCi8X2J8lOoo3oX9IuxE0=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":59,"leaf_hash":"Mwcg998v+V8g6UoBm8z6/eC3kxuIQhpLDoltmpmcsNI=","aunts":["wJ+GksGvM8TP6bX9hThZ5hALCrx0jeahl+eyZRMubFg=","Ti5WWO38ZsPvLgOKVV4EiEcCi8X2J8lOoo3oX9IuxE0=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":60,"leaf_hash":"1E4AQpfmlNTv8FWqkRwjitHnsOQoGiWyJvZup54QM98=","aunts":["sP3w+FHp+FtoP2u6DDZFeCUw3f9QFA3v9h26cdn4m3Q=","yQQmrkLqQUC5Qb3Nfs0c1GCgLhcKs/9FNWYtbNZlVDA=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":61,"leaf_hash":"sP3w+FHp+FtoP2u6DDZFeCUw3f9QFA3v9h26cdn4m3Q=","aunts":["1E4AQpfmlNTv8FWqkRwjitHnsOQoGiWyJvZup54QM98=","yQQmrkLqQUC5Qb3Nfs0c1GCgLhcKs/9FNWYtbNZlVDA=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":49,"end_row":61},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":64,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6jRKvBlCipzrEJHJusWUCdjlOvLhWNTTgD+9jP8RcUBr","/////////////////////////////////////////////////////////////////////////////7W2wwt0antw0dtJtnHC8vEnktrJYsnV/7YZglI9LvrA"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zH4Pg6O2drFgRZfeFmXqaRv/GDbfwUZQETZ3AfJpEli"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0iIKEP4vHI6TbHgZf8hzrUKT8W3mzB/QCxGZJq7/mbK"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7m411OQ2Y6xspi58QXwRzLPksTsYgpX37iMnzYq654s"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////7kQMVooMbJfBro44mEBDRq8ZpA8cMTG8IxMYCHwdap"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7Qrrdo/LdmxpAO5gBT7aZFPsYiz8RetGruKpIwbtWln"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yiUqxl0g8BBQbJE+2CuRaukkANKrbDfhjhzTJj3/aZh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7m+YYDESiYYkIA3LUZ4VHGgN1/pGPZuWGgKqv+uLNAy"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6yvtIs6Dh2/HdjJ7mkGab3YLq+TTHGseiaPbkoyMxoY"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wRknGsAf2bPKIhjIpuRrTYIQUFB5RebxeFUSm6JKJqy"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0FbSegOfEh/5/Bz36jBUmWkRTw6EQD17WmoPen6VtBh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6SiFnZoG8V5RSTNbDs/aXWeizmVRWnKfAkT/NvLchqN"],"is_max_namespace_ignored":true},{"end":87,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8X6F62rq6M3SKkzRZCHaEf6TowCG2aooWbMQf42ITjFz","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8SEozuA5BCyPqEOYrMeZTXzD/q2dz+c2MfGo5JjY4tSC","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMpwg/ozjVJAWXR/5/GWIdp3g+C4NIbePOSZOsvIevSwT","/////////////////////////////////////////////////////////////////////////////7KhpG1So7lDEnZoqOkA/wq+/SMAvIKKx1MqoSgx37AA"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Q==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8YaEbuXGDZTujjQQX3aV4yGqymV9XJewv7Pd7x02hWX2","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8WW8QqJCUUJJ3ZS08SB8mjMQH3spjBubmCxDeLdDH3Ii","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Vca17euaoWQKYwWPyixUUSKA0xAH4U6BQDrg0sK5JYc","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8dqKbuGBPwmctQHj7xBqCCU+zC3IUsbMlU7fZ6h2kMqo","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8VWNjVtehk0Wsu2jqKEiudzQgFKjQ1zewFF1NPcUFKeq","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8ZY+SkAg+mhdYf8HUkDk67D7RzXUdhVXwBCy4b+rcy35","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8Yha7aW0DJmjoxGx3i+nw07RRbf2MRXU6k9iBE9smfBu","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8dTWdpeYipBjT/Eg5KjHN0t3epjBRo9GycoAPjP/huDC","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8S/fOHIeE/4u8ZLAqOCXaROTb2IAQ6FFtCFmPvu5Sx+p","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8WdEl4OWI801OKkRqzVGjRBkq5oNKgoBcrQR4nV0Quzs","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8a21/5dwG+1BotOM1PwV2icTj1cjl3vnsKH4DgUuTUEX","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8VW9xGNetViGq7jD5xZhEEs4oP+bMGClM+rmbM91q0yr","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMsfjZqMT/6wH0zNNT5dm+1n9WcvwrsvkQBbQci7AEdrN"],"proofs":[{"total":512,"index":49,"leaf_hash":"t+6q2X4zPYpLi9PlikBy2tw8fmh/plafNz1YUl1VRlc=","aunts":["3+kuOxj/rci7mdVYxRQcbQCxy9s8E8JADvbQSKH7zO0=","B85OEuK9iTUwdHrH7BqUX5h0SDfqSyVxb63Y8qu+m68=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":50,"leaf_hash":"EMxY1XChmYRd2CVEtkRXcY7F7e+y1hxVIZ9rIw+WUEI=","aunts":["2IpDbMiOzUNy2YUuhCWbhJN3k+a0BydDhmZgYRiEbUI=","mAyGMMWSUeN+WGU7rbQIfxtjdg1+n4mprESmIUZl5R0=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":51,"leaf_hash":"2IpDbMiOzUNy2YUuhCWbhJN3k+a0BydDhmZgYRiEbUI=","aunts":["EMxY1XChmYRd2CVEtkRXcY7F7e+y1hxVIZ9rIw+WUEI=","mAyGMMWSUeN+WGU7rbQIfxtjdg1+n4mprESmIUZl5R0=","9w6zLZwCU8WkJuQCvFjczsAdPDof13STdMfAdzIHiks=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":52,"leaf_hash":"alWjUbyVBY2HJ/cVKmghcxoUMxtspp5joCRUsq0tAGA=","aunts":["dgYVKx7P/yCOYuoicKXGOoPGV72RH9cx4sb6XUJg+jc=","PgVPO8ScLNn/DzOzZkJvxbjBM8gTLy8HqlP3o1HUgaA=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":53,"leaf_hash":"dgYVKx7P/yCOYuoicKXGOoPGV72RH9cx4sb6XUJg+jc=","aunts":["alWjUbyVBY2HJ/cVKmghcxoUMxtspp5joCRUsq0tAGA=","PgVPO8ScLNn/DzOzZkJvxbjBM8gTLy8HqlP3o1HUgaA=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":54,"leaf_hash":"OYJliVYLSC+EKwtuy+jEvNMDGtAYoM3ohf0NL28EOAk=","aunts":["Wcg/AvpR78uk0U1iwUtxja5M64faClNHHaV2+7lxLMQ=","uv/uvrLuT/JWUct4DL6U9imNc5LMCuzWanxhtrjaPm0=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":55,"leaf_hash":"Wcg/AvpR78uk0U1iwUtxja5M64faClNHHaV2+7lxLMQ=","aunts":["OYJliVYLSC+EKwtuy+jEvNMDGtAYoM3ohf0NL28EOAk=","uv/uvrLuT/JWUct4DL6U9imNc5LMCuzWanxhtrjaPm0=","kUcjVvjEfg2qqHFEgK5KAj+J1DVXq6aJqvnJ58LMleY=","0jZqFqYl6hKE2q0CRwxcML1Or8Zr5MMmeXTtiO1wPzA=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":56,"leaf_hash":"J3fXscTvhou0CEI2fbQOCKFCMmbgziumXM3D/aw9614=","aunts":["jKSB1Ky+NOeu8nuMxUWKOHrhIsDLeBMOfoTDeb4QjFA=","RSfYNoiH+ZdOV7SPFG+adCg3CtVy6J7fbjriqRz20fs=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":57,"leaf_hash":"jKSB1Ky+NOeu8nuMxUWKOHrhIsDLeBMOfoTDeb4QjFA=","aunts":["J3fXscTvhou0CEI2fbQOCKFCMmbgziumXM3D/aw9614=","RSfYNoiH+ZdOV7SPFG+adCg3CtVy6J7fbjriqRz20fs=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":58,"leaf_hash":"wJ+GksGvM8TP6bX9hThZ5hALCrx0jeahl+eyZRMubFg=","aunts":["Mwcg998v+V8g6UoBm8z6/eC3kxuIQhpLDoltmpmcsNI=","Ti5WWO38ZsPvLgOKVV4EiEcCi8X2J8lOoo3oX9IuxE0=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":59,"leaf_hash":"Mwcg998v+V8g6UoBm8z6/eC3kxuIQhpLDoltmpmcsNI=","aunts":["wJ+GksGvM8TP6bX9hThZ5hALCrx0jeahl+eyZRMubFg=","Ti5WWO38ZsPvLgOKVV4EiEcCi8X2J8lOoo3oX9IuxE0=","Ymjp5wg6zMQhfwimAIH935rFkf4lMEHkuFtaBpuetB4=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":60,"leaf_hash":"1E4AQpfmlNTv8FWqkRwjitHnsOQoGiWyJvZup54QM98=","aunts":["sP3w+FHp+FtoP2u6DDZFeCUw3f9QFA3v9h26cdn4m3Q=","yQQmrkLqQUC5Qb3Nfs0c1GCgLhcKs/9FNWYtbNZlVDA=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":61,"leaf_hash":"sP3w+FHp+FtoP2u6DDZFeCUw3f9QFA3v9h26cdn4m3Q=","aunts":["1E4AQpfmlNTv8FWqkRwjitHnsOQoGiWyJvZup54QM98=","yQQmrkLqQUC5Qb3Nfs0c1GCgLhcKs/9FNWYtbNZlVDA=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":49,"end_row":61},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":96,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc9GRALXzQOZw5c7DwXIlDicISE4liKsTV+qPj40ZVujs","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3vKaUwhptdrthoj8kdQe4T+ApRB9qC1qGzcnxgp9Dwi","/////////////////////////////////////////////////////////////////////////////z9JMtR8d8P8W43+gj639fpzBGCv2viAk/AGgiWmbtS4"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+avv5fD+30lE4gbIIZjXuB1zIEFQj5TbQ+FM6Z4nCAh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zfPkA/L36mxdg1jv43aaPano7T8wZagApAp7MlpMRUW"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yOXTualXUXriMbHl345DUwytRIuG9irzKnE57xDpoXd"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yc1YPLnBUjtCbf4bc8oDLW7L9RYV/I6f735yqwVu520"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2mQLorYjzPg8aK86Moo2+WQ25XJyv7Kkmy3n2XMhSPh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xaMIPS5RFFds7dSfk9gUMCsOm3ekRzMKHlzp7wojeB6"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0pPGsnNIni+L4ZVRe5ZFuSsZ1hz91DMWm+I5UtHpsxV"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9my4/M/hOmleewuCHFgzHqMkOSJMaE1OhlvO0+G384l"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////z0pU0GPiXbvgnSPQwM2158zXq7EKSEQC+nFiF92+xWp"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////32LECWAdh3TfGerq+xS/LrKuws8RNANd3FsHz59vec7"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3dyj1bKbyyLnY9nKdSC+1rxHM7ICg6mmp6TWhuQEg7i"],"is_max_namespace_ignored":true},{"end":121,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z1iU659CaftrVQdgTTYsBynf+uHQrDdnBWvTe5ITytgD","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z/GaMsbNyxy5bKcvtcJHPidGtiH7ELStmLa+i1vnDT3U","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z3THFu/Rgczjj5skGtyjr7cz2ggbGXATrWsqC0Rsdwqh","/////////////////////////////////////////////////////////////////////////////yocZlCMZ4b+9Y/wHiJ8d+lM6GrE/YXGA1GGqSjqnqx0"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z3w9Ip5QwpmYfr6GjYYX0fuoqLh52mPV7WCEB+uNSn9C","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z7sLONHKtCqDOoXt9jCStqVz+loYFpyKthDE7yQQFygF","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z0lMxgS9pQtsPkvvXA4denbKiU+2U0zO6pFLZLDSMylP","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zzWKHlUShV7Yjp9hg0+KJTvhZwyIJ3C8BnzmLnYcqIFn","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zzLyfjXt+FsCAazB8mlcNcgsttZd08ZPwWkrOT1SrZu4","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z0ZxdZDGBzrX797dAnqVFFINVkYW6BhJlTwskhtE/2Bv","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z4y1GNI64qIvyS9dgty4y0rLIqrEplxWp2KJFIj9lme9","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z0SNRp3Lfv2YWtnoZ8w9Gsy1klgrH4RhoID+vJrvYDIK","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zwuFQ1P9o4Z+uw+vilcpb4G8eiuZ7baohPhmHMaJHi10","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z+dOUV821GjgwDu+tH+ZWMhy/THXSNG8pt0XSlZtK+N8","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zyOGE4EDoPhuLP7iORRvcXgtq5EAF1ngQRtIkv7j+3h0","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z7GtoQFhFdtancKzHNGI49kB0NYXVjSXYS+TyFEb/Z8y","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z/g7Q69jq4KTTY+hWlem/0NLqqAXt8ODGePPlfxx5fUi"],"proofs":[{"total":512,"index":86,"leaf_hash":"OjATVN9Veg7NJFZIRVpu+UY0NuHCQvsBe5OJRKX9OyE=","aunts":["irJQKgIq44t+ynw82tq4EbfmOX+YqEpvcbXqz76HvLc=","Z3gAYFQ+vsI2+cl3q6UqGuerebvPdm5W7dkxrPFsxzU=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":87,"leaf_hash":"irJQKgIq44t+ynw82tq4EbfmOX+YqEpvcbXqz76HvLc=","aunts":["OjATVN9Veg7NJFZIRVpu+UY0NuHCQvsBe5OJRKX9OyE=","Z3gAYFQ+vsI2+cl3q6UqGuerebvPdm5W7dkxrPFsxzU=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":88,"leaf_hash":"DuOdQvjZkxPuKUl8z/hYi/G9K+RCv65yYq9ua5GY5RY=","aunts":["08jEpZu+o7oqpZEd0cEyEysFjJz/QIVUmWdNKBNBbJ0=","m8g+inpc1NDHkdt8f9hs+Afh4XPePF1E09bHqN7L0a0=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":89,"leaf_hash":"08jEpZu+o7oqpZEd0cEyEysFjJz/QIVUmWdNKBNBbJ0=","aunts":["DuOdQvjZkxPuKUl8z/hYi/G9K+RCv65yYq9ua5GY5RY=","m8g+inpc1NDHkdt8f9hs+Afh4XPePF1E09bHqN7L0a0=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":90,"leaf_hash":"pHMNYYaE05b3FZxmShYZgv+3SS3tB4gjYh3IptXtckA=","aunts":["lCHOb+Ptyu8Bv/8eF3ppQ2VfY3AMJ2j4uepYGw3Vz3Q=","g+L14pCLBXYKpGi0X5eV0BdvSlv1oTXsCGxacNVZGH4=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":91,"leaf_hash":"lCHOb+Ptyu8Bv/8eF3ppQ2VfY3AMJ2j4uepYGw3Vz3Q=","aunts":["pHMNYYaE05b3FZxmShYZgv+3SS3tB4gjYh3IptXtckA=","g+L14pCLBXYKpGi0X5eV0BdvSlv1oTXsCGxacNVZGH4=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":92,"leaf_hash":"qiDew+jeCGtNVtHE5ZDMWgeQVW2HFjGSuPqSlrR6h9g=","aunts":["39Pv0HHLs8UHxYpSov5DNiCMheYZ1jRwoLcwluEY9qI=","EdQLk3bztQoL9ftIsY4QG/7/FdIGmPYmTIjscqn1pUI=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":93,"leaf_hash":"39Pv0HHLs8UHxYpSov5DNiCMheYZ1jRwoLcwluEY9qI=","aunts":["qiDew+jeCGtNVtHE5ZDMWgeQVW2HFjGSuPqSlrR6h9g=","EdQLk3bztQoL9ftIsY4QG/7/FdIGmPYmTIjscqn1pUI=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":94,"leaf_hash":"UuehJtrgSxPUOZB7MFBjGZi5nNKaXl31WzK6qsIOC48=","aunts":["1LWnWyoR2xZkTycdjCHEtE0KQ0sXZz8lUb4EsTujwR0=","5gjpMP4ajs9ew5SpcS6REOJAWcHm4H3ou2RudeoklYs=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":95,"leaf_hash":"1LWnWyoR2xZkTycdjCHEtE0KQ0sXZz8lUb4EsTujwR0=","aunts":["UuehJtrgSxPUOZB7MFBjGZi5nNKaXl31WzK6qsIOC48=","5gjpMP4ajs9ew5SpcS6REOJAWcHm4H3ou2RudeoklYs=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":96,"leaf_hash":"j4hLrenlE0Yj3zJ/NR8jBjeAEQCtQt6Ez4Nx1EVuO94=","aunts":["Hqyzz7yo6IdrK2ZTnnYUOMupQ4MnzTWqo9JNF3KVnRQ=","9yr01b1WteunTBSDdo0uRhlGCmzDZeEIMojHpwnMGsA=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":97,"leaf_hash":"Hqyzz7yo6IdrK2ZTnnYUOMupQ4MnzTWqo9JNF3KVnRQ=","aunts":["j4hLrenlE0Yj3zJ/NR8jBjeAEQCtQt6Ez4Nx1EVuO94=","9yr01b1WteunTBSDdo0uRhlGCmzDZeEIMojHpwnMGsA=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":98,"leaf_hash":"1q55h49DWuLAx633Byb5EiV39vm+LOYTohtaugeN0sg=","aunts":["mOSNHBdiJ/M9gS6YIItj9EOdjuJdbco0brtkF/ZN6Rk=","eX4pk5V+1XNpOVhsP8MaoZvan9eQ2A4cTi/1xeHjzQ8=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":86,"end_row":98},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":96,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc9GRALXzQOZw5c7DwXIlDicISE4liKsTV+qPj40ZVujs","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3vKaUwhptdrthoj8kdQe4T+ApRB9qC1qGzcnxgp9Dwi","/////////////////////////////////////////////////////////////////////////////z9JMtR8d8P8W43+gj639fpzBGCv2viAk/AGgiWmbtS4"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+avv5fD+30lE4gbIIZjXuB1zIEFQj5TbQ+FM6Z4nCAh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zfPkA/L36mxdg1jv43aaPano7T8wZagApAp7MlpMRUW"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yOXTualXUXriMbHl345DUwytRIuG9irzKnE57xDpoXd"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yc1YPLnBUjtCbf4bc8oDLW7L9RYV/I6f735yqwVu520"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2mQLorYjzPg8aK86Moo2+WQ25XJyv7Kkmy3n2XMhSPh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xaMIPS5RFFds7dSfk9gUMCsOm3ekRzMKHlzp7wojeB6"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0pPGsnNIni+L4ZVRe5ZFuSsZ1hz91DMWm+I5UtHpsxV"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9my4/M/hOmleewuCHFgzHqMkOSJMaE1OhlvO0+G384l"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////z0pU0GPiXbvgnSPQwM2158zXq7EKSEQC+nFiF92+xWp"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////32LECWAdh3TfGerq+xS/LrKuws8RNANd3FsHz59vec7"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3dyj1bKbyyLnY9nKdSC+1rxHM7ICg6mmp6TWhuQEg7i"],"is_max_namespace_ignored":true},{"end":121,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z1iU659CaftrVQdgTTYsBynf+uHQrDdnBWvTe5ITytgD","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z/GaMsbNyxy5bKcvtcJHPidGtiH7ELStmLa+i1vnDT3U","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z3THFu/Rgczjj5skGtyjr7cz2ggbGXATrWsqC0Rsdwqh","/////////////////////////////////////////////////////////////////////////////yocZlCMZ4b+9Y/wHiJ8d+lM6GrE/YXGA1GGqSjqnqx0"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z3w9Ip5QwpmYfr6GjYYX0fuoqLh52mPV7WCEB+uNSn9C","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z7sLONHKtCqDOoXt9jCStqVz+loYFpyKthDE7yQQFygF","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z0lMxgS9pQtsPkvvXA4denbKiU+2U0zO6pFLZLDSMylP","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zzWKHlUShV7Yjp9hg0+KJTvhZwyIJ3C8BnzmLnYcqIFn","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zzLyfjXt+FsCAazB8mlcNcgsttZd08ZPwWkrOT1SrZu4","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z0ZxdZDGBzrX797dAnqVFFINVkYW6BhJlTwskhtE/2Bv","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z4y1GNI64qIvyS9dgty4y0rLIqrEplxWp2KJFIj9lme9","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z0SNRp3Lfv2YWtnoZ8w9Gsy1klgrH4RhoID+vJrvYDIK","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zwuFQ1P9o4Z+uw+vilcpb4G8eiuZ7baohPhmHMaJHi10","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z+dOUV821GjgwDu+tH+ZWMhy/THXSNG8pt0XSlZtK+N8","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/zyOGE4EDoPhuLP7iORRvcXgtq5EAF1ngQRtIkv7j+3h0","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z7GtoQFhFdtancKzHNGI49kB0NYXVjSXYS+TyFEb/Z8y","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z/g7Q69jq4KTTY+hWlem/0NLqqAXt8ODGePPlfxx5fUi"],"proofs":[{"total":512,"index":86,"leaf_hash":"OjATVN9Veg7NJFZIRVpu+UY0NuHCQvsBe5OJRKX9OyE=","aunts":["irJQKgIq44t+ynw82tq4EbfmOX+YqEpvcbXqz76HvLc=","Z3gAYFQ+vsI2+cl3q6UqGuerebvPdm5W7dkxrPFsxzU=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":87,"leaf_hash":"irJQKgIq44t+ynw82tq4EbfmOX+YqEpvcbXqz76HvLc=","aunts":["OjATVN9Veg7NJFZIRVpu+UY0NuHCQvsBe5OJRKX9OyE=","Z3gAYFQ+vsI2+cl3q6UqGuerebvPdm5W7dkxrPFsxzU=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":88,"leaf_hash":"DuOdQvjZkxPuKUl8z/hYi/G9K+RCv65yYq9ua5GY5RY=","aunts":["08jEpZu+o7oqpZEd0cEyEysFjJz/QIVUmWdNKBNBbJ0=","m8g+inpc1NDHkdt8f9hs+Afh4XPePF1E09bHqN7L0a0=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":89,"leaf_hash":"08jEpZu+o7oqpZEd0cEyEysFjJz/QIVUmWdNKBNBbJ0=","aunts":["DuOdQvjZkxPuKUl8z/hYi/G9K+RCv65yYq9ua5GY5RY=","m8g+inpc1NDHkdt8f9hs+Afh4XPePF1E09bHqN7L0a0=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":90,"leaf_hash":"pHMNYYaE05b3FZxmShYZgv+3SS3tB4gjYh3IptXtckA=","aunts":["lCHOb+Ptyu8Bv/8eF3ppQ2VfY3AMJ2j4uepYGw3Vz3Q=","g+L14pCLBXYKpGi0X5eV0BdvSlv1oTXsCGxacNVZGH4=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":91,"leaf_hash":"lCHOb+Ptyu8Bv/8eF3ppQ2VfY3AMJ2j4uepYGw3Vz3Q=","aunts":["pHMNYYaE05b3FZxmShYZgv+3SS3tB4gjYh3IptXtckA=","g+L14pCLBXYKpGi0X5eV0BdvSlv1oTXsCGxacNVZGH4=","raWCAns4bETP5B8n6LjVBlr/Sb9R6H6LUThoLXdvliI=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":92,"leaf_hash":"qiDew+jeCGtNVtHE5ZDMWgeQVW2HFjGSuPqSlrR6h9g=","aunts":["39Pv0HHLs8UHxYpSov5DNiCMheYZ1jRwoLcwluEY9qI=","EdQLk3bztQoL9ftIsY4QG/7/FdIGmPYmTIjscqn1pUI=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":93,"leaf_hash":"39Pv0HHLs8UHxYpSov5DNiCMheYZ1jRwoLcwluEY9qI=","aunts":["qiDew+jeCGtNVtHE5ZDMWgeQVW2HFjGSuPqSlrR6h9g=","EdQLk3bztQoL9ftIsY4QG/7/FdIGmPYmTIjscqn1pUI=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":94,"leaf_hash":"UuehJtrgSxPUOZB7MFBjGZi5nNKaXl31WzK6qsIOC48=","aunts":["1LWnWyoR2xZkTycdjCHEtE0KQ0sXZz8lUb4EsTujwR0=","5gjpMP4ajs9ew5SpcS6REOJAWcHm4H3ou2RudeoklYs=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":95,"leaf_hash":"1LWnWyoR2xZkTycdjCHEtE0KQ0sXZz8lUb4EsTujwR0=","aunts":["UuehJtrgSxPUOZB7MFBjGZi5nNKaXl31WzK6qsIOC48=","5gjpMP4ajs9ew5SpcS6REOJAWcHm4H3ou2RudeoklYs=","5q0tU2g2NuIJIJjnUfGDDMUvYkqqw08dL6q2Exsb4TU=","ESwvasdI9622AGvvf7rKWP0Uk2pBGHjltjHgaKKzynI=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":96,"leaf_hash":"j4hLrenlE0Yj3zJ/NR8jBjeAEQCtQt6Ez4Nx1EVuO94=","aunts":["Hqyzz7yo6IdrK2ZTnnYUOMupQ4MnzTWqo9JNF3KVnRQ=","9yr01b1WteunTBSDdo0uRhlGCmzDZeEIMojHpwnMGsA=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":97,"leaf_hash":"Hqyzz7yo6IdrK2ZTnnYUOMupQ4MnzTWqo9JNF3KVnRQ=","aunts":["j4hLrenlE0Yj3zJ/NR8jBjeAEQCtQt6Ez4Nx1EVuO94=","9yr01b1WteunTBSDdo0uRhlGCmzDZeEIMojHpwnMGsA=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":98,"leaf_hash":"1q55h49DWuLAx633Byb5EiV39vm+LOYTohtaugeN0sg=","aunts":["mOSNHBdiJ/M9gS6YIItj9EOdjuJdbco0brtkF/ZN6Rk=","eX4pk5V+1XNpOVhsP8MaoZvan9eQ2A4cTi/1xeHjzQ8=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":86,"end_row":98},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":64,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOdC7LxXfUbhXAEObqruc02QdiBbBNmFildGNFACtn0P","//////////////////////////////////////////////////////////////////////////////3h5tvoml946LbF1tuJtMITVtdHwAnUPB4rQYbfnlzj"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xSqrHHGZsdC3oL8izDDfPx3Mt2KqeoezlwwG5v1Xdz7"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xzTBlaWqLHvcNv291OAKa7baL6WrgGxG/QnN6zOjyV0"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////w5zoH0ThQvUSKOUvy45STtw8eF3e8kjmi0RIqAoAQN3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zQ2efPD3mMjnq56pfVaLd6tXUKo3Lx91nnpE4Pi9FAs"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6Yh9ZAX4HrjOKHsyjraGwGSkHm9LvOK+dpKOykb7vEb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////GABNUCt0C2y/L1xcHO5Fta4mDT44Q6ls7JV9K/8gmY"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////84bBYzJD94gbc/t/T1DH1UXqY6DPgu4nG4TFVXnZYII"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7KY5llhATdtmpUWIrNwooPtNgZZJR+5hAQQNzg3KXHP"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////73G8fx12UDzTTvcmsOnMXDbHD7Ja/OUGlP8RIdP1Osb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0P/mOETG9MsDHQZLctrJL+y0vRLEYBR6m5qIFtkKgPF"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0rOa6MlxECMYRllQWA/76R1fHRSEM2DOfcNbKCTEMAF"],"is_max_namespace_ignored":true},{"end":91,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314u+EpS5OrojM6nST3lzfqDdaxuP3TUyusWCsWvDOddkW","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314tsRWzTO2ZfjPB0x3P59fdCNMWEo5d0/zgk1ms6z9PU8","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizoMRnADUjnsGYvOG+kqzsX10Qw7AtmzKeANYNGgwUMtT","/////////////////////////////////////////////////////////////////////////////7XDREmTiwZwpguDCbCDaWadKNGBLSrXGDdV23EJsKhS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314mjwKPyTs9huTlUTWlKcsLrr+/LIS86LrGnMHfuPKUvO","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314l5DMPrsy4djo/7tUNF4TKy88gN1OlOhNO4fevZWwZL6","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314j+hyS7GlD/HbqtQSA4snLZVjJH3DggmOGsnU6ISfpyu","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314ssNHFO2Unoia48H9ExEpugZy+Sjjb0m4M/YgOqBThd1","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314t3LmTHsOqK6a1dXjvHcRM3wgWWKd7K/mP3MZSTkazy4","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314sPoBncasN3a6mgLg5mhotPDQWFckBV/8Mto8enJgOa5","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314qAkvdoXDP3kJg294hPHAaoiqCQDfbDm49jj04sUaQV8","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314r7nzOFwQJ+Sew+TJnGsFkhhTswwPW8akQC+i4cy2nXN","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314nR+911qMGCQ2F+NWSdSzJSHvaOo0z0prSVFvRuWteIJ","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314uHQaHsJKIiCK5m9s1hIBKdtX1sFQ0263tgvSCPUPQDn","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314hWSCMKQLg1ANNjjh1gp8EVldqbpicE+7bvM4vkunumk","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314mtDlAK18BTziX8B9UQFhRB2tTX6CSNGYGbQ4mqi/Cav","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrEFVEynmuRAbggTEYYqyVtjoA+pubEy+NMwNDeP7LZt"],"proofs":[{"total":512,"index":12,"leaf_hash":"0B5qRFQ4zr5aArC+LA8dhvY5DgJ9qELdo4gJ8SFwqow=","aunts":["k6rFtu9eHe2Lu3cwSUylBTOL8pCWUeJPI3Eh5u6y8YA=","VFk3nrGeCwpnY8sdiv9RvLrglQjbDPVeZK7DcmOkfwA=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":13,"leaf_hash":"k6rFtu9eHe2Lu3cwSUylBTOL8pCWUeJPI3Eh5u6y8YA=","aunts":["0B5qRFQ4zr5aArC+LA8dhvY5DgJ9qELdo4gJ8SFwqow=","VFk3nrGeCwpnY8sdiv9RvLrglQjbDPVeZK7DcmOkfwA=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":14,"leaf_hash":"KkKHRV+sh0xYL951DAdpF60MUoZrh9t4CFFoe8BsZO4=","aunts":["K62OY0ZlRM3IxHbwa0NCD3GnEMEDEtkSKdP+pkt3+AM=","nawYDuPM4SluuN+1NdoesvuHv3QOQQl0N8DycyheKmc=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":15,"leaf_hash":"K62OY0ZlRM3IxHbwa0NCD3GnEMEDEtkSKdP+pkt3+AM=","aunts":["KkKHRV+sh0xYL951DAdpF60MUoZrh9t4CFFoe8BsZO4=","nawYDuPM4SluuN+1NdoesvuHv3QOQQl0N8DycyheKmc=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":16,"leaf_hash":"B3ikrq5xMOKy1pV8xzzoY3fx8vOlJsmcYq8Xym8ntwk=","aunts":["29je9n/I5MzEYb21drsDO2d3bfUwKxC8pN5VAHgNVF8=","lM2HiT9UHlQGkP03g0rpZ8GCD4oAiHDQlq/HmIXY6Q8=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":17,"leaf_hash":"29je9n/I5MzEYb21drsDO2d3bfUwKxC8pN5VAHgNVF8=","aunts":["B3ikrq5xMOKy1pV8xzzoY3fx8vOlJsmcYq8Xym8ntwk=","lM2HiT9UHlQGkP03g0rpZ8GCD4oAiHDQlq/HmIXY6Q8=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":18,"leaf_hash":"CuEObNx1lkSv7jx1TzxuJD2cQJB6AKD14dgpHlc0hOE=","aunts":["xNzHO0JW8rqvrQh9rhnpup+Q2X0e4JgzcW3LJN1UIlM=","78Ca5ohLTsExpvCMqboFRI1og/eBiYzR6qOSE73k6Ps=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":19,"leaf_hash":"xNzHO0JW8rqvrQh9rhnpup+Q2X0e4JgzcW3LJN1UIlM=","aunts":["CuEObNx1lkSv7jx1TzxuJD2cQJB6AKD14dgpHlc0hOE=","78Ca5ohLTsExpvCMqboFRI1og/eBiYzR6qOSE73k6Ps=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":20,"leaf_hash":"Oi2oIwhoicq0bW/6a+UxCPY+osX4Uv6GeSdshJuBkvo=","aunts":["VvCwQB7nn0bpfdR42iokckxJu8EYPuKuRJXteIfNlkQ=","7r0rmSuANHHqDnnlak+QkyOLOnvk/BBOrcuwekZE8gc=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":21,"leaf_hash":"VvCwQB7nn0bpfdR42iokckxJu8EYPuKuRJXteIfNlkQ=","aunts":["Oi2oIwhoicq0bW/6a+UxCPY+osX4Uv6GeSdshJuBkvo=","7r0rmSuANHHqDnnlak+QkyOLOnvk/BBOrcuwekZE8gc=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":22,"leaf_hash":"z9pFN0B7FpyXcDKpjnfGfhiDgcWUAe0F6RB1AyFPxEI=","aunts":["Wtc1BiVRMkFhZq8TlP1qE0dt0gXVJ4vYNbLhG/cdQ5Y=","pLBny7aBOqmIDzyaeEFCqDOswKJSxEEN89QnxdEhygA=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":23,"leaf_hash":"Wtc1BiVRMkFhZq8TlP1qE0dt0gXVJ4vYNbLhG/cdQ5Y=","aunts":["z9pFN0B7FpyXcDKpjnfGfhiDgcWUAe0F6RB1AyFPxEI=","pLBny7aBOqmIDzyaeEFCqDOswKJSxEEN89QnxdEhygA=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":24,"leaf_hash":"4DOlpLonw/S8WfZGA8Ta9r3L1/6a2jJVi1v3os6HsdE=","aunts":["jC0w1LLr8sVZGsQbfAFGJxIJ7MrVblPMgREbKKKhr0U=","lUIHwdjN0WX5pTX3QjKoAJywZOtft8jCaYItP36T0dk=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":12,"end_row":24},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":64,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOdC7LxXfUbhXAEObqruc02QdiBbBNmFildGNFACtn0P","//////////////////////////////////////////////////////////////////////////////3h5tvoml946LbF1tuJtMITVtdHwAnUPB4rQYbfnlzj"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xSqrHHGZsdC3oL8izDDfPx3Mt2KqeoezlwwG5v1Xdz7"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xzTBlaWqLHvcNv291OAKa7baL6WrgGxG/QnN6zOjyV0"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////w5zoH0ThQvUSKOUvy45STtw8eF3e8kjmi0RIqAoAQN3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zQ2efPD3mMjnq56pfVaLd6tXUKo3Lx91nnpE4Pi9FAs"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6Yh9ZAX4HrjOKHsyjraGwGSkHm9LvOK+dpKOykb7vEb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////GABNUCt0C2y/L1xcHO5Fta4mDT44Q6ls7JV9K/8gmY"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////84bBYzJD94gbc/t/T1DH1UXqY6DPgu4nG4TFVXnZYII"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7KY5llhATdtmpUWIrNwooPtNgZZJR+5hAQQNzg3KXHP"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////73G8fx12UDzTTvcmsOnMXDbHD7Ja/OUGlP8RIdP1Osb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0P/mOETG9MsDHQZLctrJL+y0vRLEYBR6m5qIFtkKgPF"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0rOa6MlxECMYRllQWA/76R1fHRSEM2DOfcNbKCTEMAF"],"is_max_namespace_ignored":true},{"end":91,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314u+EpS5OrojM6nST3lzfqDdaxuP3TUyusWCsWvDOddkW","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314tsRWzTO2ZfjPB0x3P59fdCNMWEo5d0/zgk1ms6z9PU8","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizoMRnADUjnsGYvOG+kqzsX10Qw7AtmzKeANYNGgwUMtT","/////////////////////////////////////////////////////////////////////////////7XDREmTiwZwpguDCbCDaWadKNGBLSrXGDdV23EJsKhS"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314g==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314mjwKPyTs9huTlUTWlKcsLrr+/LIS86LrGnMHfuPKUvO","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314l5DMPrsy4djo/7tUNF4TKy88gN1OlOhNO4fevZWwZL6","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314j+hyS7GlD/HbqtQSA4snLZVjJH3DggmOGsnU6ISfpyu","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314ssNHFO2Unoia48H9ExEpugZy+Sjjb0m4M/YgOqBThd1","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314t3LmTHsOqK6a1dXjvHcRM3wgWWKd7K/mP3MZSTkazy4","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314sPoBncasN3a6mgLg5mhotPDQWFckBV/8Mto8enJgOa5","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314qAkvdoXDP3kJg294hPHAaoiqCQDfbDm49jj04sUaQV8","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314r7nzOFwQJ+Sew+TJnGsFkhhTswwPW8akQC+i4cy2nXN","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314nR+911qMGCQ2F+NWSdSzJSHvaOo0z0prSVFvRuWteIJ","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314uHQaHsJKIiCK5m9s1hIBKdtX1sFQ0263tgvSCPUPQDn","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314hWSCMKQLg1ANNjjh1gp8EVldqbpicE+7bvM4vkunumk","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314mtDlAK18BTziX8B9UQFhRB2tTX6CSNGYGbQ4mqi/Cav","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrEFVEynmuRAbggTEYYqyVtjoA+pubEy+NMwNDeP7LZt"],"proofs":[{"total":512,"index":12,"leaf_hash":"0B5qRFQ4zr5aArC+LA8dhvY5DgJ9qELdo4gJ8SFwqow=","aunts":["k6rFtu9eHe2Lu3cwSUylBTOL8pCWUeJPI3Eh5u6y8YA=","VFk3nrGeCwpnY8sdiv9RvLrglQjbDPVeZK7DcmOkfwA=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":13,"leaf_hash":"k6rFtu9eHe2Lu3cwSUylBTOL8pCWUeJPI3Eh5u6y8YA=","aunts":["0B5qRFQ4zr5aArC+LA8dhvY5DgJ9qELdo4gJ8SFwqow=","VFk3nrGeCwpnY8sdiv9RvLrglQjbDPVeZK7DcmOkfwA=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":14,"leaf_hash":"KkKHRV+sh0xYL951DAdpF60MUoZrh9t4CFFoe8BsZO4=","aunts":["K62OY0ZlRM3IxHbwa0NCD3GnEMEDEtkSKdP+pkt3+AM=","nawYDuPM4SluuN+1NdoesvuHv3QOQQl0N8DycyheKmc=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":15,"leaf_hash":"K62OY0ZlRM3IxHbwa0NCD3GnEMEDEtkSKdP+pkt3+AM=","aunts":["KkKHRV+sh0xYL951DAdpF60MUoZrh9t4CFFoe8BsZO4=","nawYDuPM4SluuN+1NdoesvuHv3QOQQl0N8DycyheKmc=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":16,"leaf_hash":"B3ikrq5xMOKy1pV8xzzoY3fx8vOlJsmcYq8Xym8ntwk=","aunts":["29je9n/I5MzEYb21drsDO2d3bfUwKxC8pN5VAHgNVF8=","lM2HiT9UHlQGkP03g0rpZ8GCD4oAiHDQlq/HmIXY6Q8=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":17,"leaf_hash":"29je9n/I5MzEYb21drsDO2d3bfUwKxC8pN5VAHgNVF8=","aunts":["B3ikrq5xMOKy1pV8xzzoY3fx8vOlJsmcYq8Xym8ntwk=","lM2HiT9UHlQGkP03g0rpZ8GCD4oAiHDQlq/HmIXY6Q8=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":18,"leaf_hash":"CuEObNx1lkSv7jx1TzxuJD2cQJB6AKD14dgpHlc0hOE=","aunts":["xNzHO0JW8rqvrQh9rhnpup+Q2X0e4JgzcW3LJN1UIlM=","78Ca5ohLTsExpvCMqboFRI1og/eBiYzR6qOSE73k6Ps=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":19,"leaf_hash":"xNzHO0JW8rqvrQh9rhnpup+Q2X0e4JgzcW3LJN1UIlM=","aunts":["CuEObNx1lkSv7jx1TzxuJD2cQJB6AKD14dgpHlc0hOE=","78Ca5ohLTsExpvCMqboFRI1og/eBiYzR6qOSE73k6Ps=","iW3qrBUCcmUQ78zB9pqUx8zHSM9d1avtHwe0oqXkcXk=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":20,"leaf_hash":"Oi2oIwhoicq0bW/6a+UxCPY+osX4Uv6GeSdshJuBkvo=","aunts":["VvCwQB7nn0bpfdR42iokckxJu8EYPuKuRJXteIfNlkQ=","7r0rmSuANHHqDnnlak+QkyOLOnvk/BBOrcuwekZE8gc=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":21,"leaf_hash":"VvCwQB7nn0bpfdR42iokckxJu8EYPuKuRJXteIfNlkQ=","aunts":["Oi2oIwhoicq0bW/6a+UxCPY+osX4Uv6GeSdshJuBkvo=","7r0rmSuANHHqDnnlak+QkyOLOnvk/BBOrcuwekZE8gc=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":22,"leaf_hash":"z9pFN0B7FpyXcDKpjnfGfhiDgcWUAe0F6RB1AyFPxEI=","aunts":["Wtc1BiVRMkFhZq8TlP1qE0dt0gXVJ4vYNbLhG/cdQ5Y=","pLBny7aBOqmIDzyaeEFCqDOswKJSxEEN89QnxdEhygA=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":23,"leaf_hash":"Wtc1BiVRMkFhZq8TlP1qE0dt0gXVJ4vYNbLhG/cdQ5Y=","aunts":["z9pFN0B7FpyXcDKpjnfGfhiDgcWUAe0F6RB1AyFPxEI=","pLBny7aBOqmIDzyaeEFCqDOswKJSxEEN89QnxdEhygA=","tax8dVN0ivOs+s5xXqkiuEmoRmOuvhfQaz3dGE9OHwU=","iZHTacwiY3xYv4yGqdbhgVsHl0S2rtL1me0iR6pEJjc=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":24,"leaf_hash":"4DOlpLonw/S8WfZGA8Ta9r3L1/6a2jJVi1v3os6HsdE=","aunts":["jC0w1LLr8sVZGsQbfAFGJxIJ7MrVblPMgREbKKKhr0U=","lUIHwdjN0WX5pTX3QjKoAJywZOtft8jCaYItP36T0dk=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":12,"end_row":24},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6QvVuGXa2RMRszSxRLGYfXioOzNFPXq+q/c0ptuu2zc"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8kGGejdIDNAbMz600F/Mr7/d0m42Yzj/8MVuQVO1P4n"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xBoFfGwXh2EhyejEwq5KfWax/LMjoVDTvf2YwhhaUHS"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+Pey5g0o/3bRNaTs0272SWm3Tx0toiMweizmHnEqVeq"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2VYTRlyVxv11AwdhqsZxe/cJgBxrVubb5BGFVYSSXdz"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wxDVDyYiBpjizK1zmBG6JllMdYzQzMACgTTvLqe60fk"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xr76OkC+B0u96yPTHdjn1TlaVz0hLCWkXbmX5fNG8ni"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6X7VTF1tniyWgO0NI4tdIE1CskUbG9tnz6q70xWb1WE"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3QdRJjhvMApRr+6YBLr6adJVK2XDGg3vdy8gaNCeb12"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3d6P+TVefscPa5wIAVVNTLaMxDoTgWXBcX2O2peVhFO"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2+IQuFJDs7DlDHBICdJ/zCr1XYeclDIcUj+eBFgTaPh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wP4wfKnJNSieTFslO9K8cyP3Ic84zdFn0mgrDqCwq+O"],"is_max_namespace_ignored":true},{"end":29,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJKkj/q2BG/p5ItDnQxZkZChmI6SOwmke7maj/vxrFor1","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJFX9my5t8+w8CJADYZxXnIbGaTqliMflKr1Gt0aWGivF","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M1w/csEfKazrcsUaqGaAqVCGvP8FXDjihNF10oiZuzOJ","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MwAPg9SrSSlKIn1I/auROIJobGRoPjt36aTVToI46BIu","/////////////////////////////////////////////////////////////////////////////8kk8Gqbi+yE3sPauA988PQ2hY8JMoDrm+RNTTclbXOW"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJDqJU5iBOe435h9UIGv80ylQz4ygl4HIiz5gOFyvqXly","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJJAaz/b68Eur1yENb+9kGE1z/T9lNLQjT65HzoNMAJb9","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJBSk6Ay3n+Wd+RvKUGlCdb0dcYeYtNfkn3Qzws6cB+93","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJLhIt5NpVq8g5rD+BNIGrFz5engAxc7MMBhR63CFmHpR","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJIuiB/ktqzuVkQku0xrIJGW8Fl96qfdGbrw+ndai2Hta","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJLkreiuAVLSI2aGiy2KatU7vobVjAqDQvQ+SDEl2tiRe","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJA3Cf7ss2SiaperjLHIUGelgXk8y59nqGTUtebNdTXCt","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJFWC7oMF11nVfVOiemU8xpOR/CbxbNAIe0Ry0QBoNFbl","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJD+bOta/DEmL0KeUUqPh44kaR4NXBps9SxhBEwy1y8qD","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJAMPpaEA+Mb0EkXtXvy5XwSeq6F0OBBdIjJEiaI6TVSI","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJG8huxN10q1Mpvk0jSMQvxHekrkV//lJESfuwvGV3/kI","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJJ3UFv79yIa54IN/PxpLk3BHyaTrhudMliLEk/RPZEig","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MxqhQlHwx6G2tCz73LzgB7Fe3l85116D9Y2ytzCPR6nq"],"proofs":[{"total":512,"index":99,"leaf_hash":"mOSNHBdiJ/M9gS6YIItj9EOdjuJdbco0brtkF/ZN6Rk=","aunts":["1q55h49DWuLAx633Byb5EiV39vm+LOYTohtaugeN0sg=","eX4pk5V+1XNpOVhsP8MaoZvan9eQ2A4cTi/1xeHjzQ8=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":100,"leaf_hash":"V+8rW8z9SRrM6rxr5w8I6VOrF4pRVaiyvYT5VrPAh8c=","aunts":["Kqg95hzXdob5ByTSRJkhJOwMrIK6NFUlioqZ6UuiXjQ=","FodCi6Qvtr92FygebPwvbI+hI1j+78SlLS6ouuIlOz4=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":101,"leaf_hash":"Kqg95hzXdob5ByTSRJkhJOwMrIK6NFUlioqZ6UuiXjQ=","aunts":["V+8rW8z9SRrM6rxr5w8I6VOrF4pRVaiyvYT5VrPAh8c=","FodCi6Qvtr92FygebPwvbI+hI1j+78SlLS6ouuIlOz4=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":102,"leaf_hash":"0lrE1glNSdD62EcKeEF7OzUPuehaPhhLyQLtczmVKpk=","aunts":["IsxW2PDXa87CS25GRv9vgih7BTotz0gbWkA/FwO4ADI=","xjS1mobRlxcPLVZ0oP+4O5TA1ehbCkUkrdRCSXw/f90=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":103,"leaf_hash":"IsxW2PDXa87CS25GRv9vgih7BTotz0gbWkA/FwO4ADI=","aunts":["0lrE1glNSdD62EcKeEF7OzUPuehaPhhLyQLtczmVKpk=","xjS1mobRlxcPLVZ0oP+4O5TA1ehbCkUkrdRCSXw/f90=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":104,"leaf_hash":"yIN812UKJWUFudwFKjEpJsngLDw1jz9W8s3hHxsrhmE=","aunts":["Jqz2CC+PyImkPxu/EGRp8pq8uBTGZ1g79gqYLYfkMfo=","V9rTIsdMdcq+SiMqNse7z6xEtkjCGLYPB2jAuPM49oA=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":105,"leaf_hash":"Jqz2CC+PyImkPxu/EGRp8pq8uBTGZ1g79gqYLYfkMfo=","aunts":["yIN812UKJWUFudwFKjEpJsngLDw1jz9W8s3hHxsrhmE=","V9rTIsdMdcq+SiMqNse7z6xEtkjCGLYPB2jAuPM49oA=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":106,"leaf_hash":"6tNm2Mb6K117jRBNmPMJh/lRVWntKB8j2/vhUwhqOwA=","aunts":["B//Vf13XpnPAb5ILZ1JFC/3X/NzELsuaRci7c6nlCrQ=","1SdHCpTre0I49VkZPTjXeB7sK/wKNrHpkR7Kv0Rnylk=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":107,"leaf_hash":"B//Vf13XpnPAb5ILZ1JFC/3X/NzELsuaRci7c6nlCrQ=","aunts":["6tNm2Mb6K117jRBNmPMJh/lRVWntKB8j2/vhUwhqOwA=","1SdHCpTre0I49VkZPTjXeB7sK/wKNrHpkR7Kv0Rnylk=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":108,"leaf_hash":"P1o9UzUZb2chjKzfkjgjN3b6D3cpKeMJRZhyRUXOj8Y=","aunts":["qY7DjvfAdATYAUf3OaysC92TZf8E3ud9l210+Y3iGgU=","ow+/vaWMmrqRFYx1qCIRF1ZND2oFTRTMW51xiNDCOQk=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":109,"leaf_hash":"qY7DjvfAdATYAUf3OaysC92TZf8E3ud9l210+Y3iGgU=","aunts":["P1o9UzUZb2chjKzfkjgjN3b6D3cpKeMJRZhyRUXOj8Y=","ow+/vaWMmrqRFYx1qCIRF1ZND2oFTRTMW51xiNDCOQk=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":110,"leaf_hash":"bvB/IyNsdtU7RXBexyehw+Q2TGHxJAPYXBR+6lDmhvs=","aunts":["lon6nxclYKAVITg9p7dh52lNBSs5yWra77MJ5/FTimI=","wq5R+bTkgHH2KMWIQu61uVGfgxiGIcJ+NCyVHWgfHqE=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":111,"leaf_hash":"lon6nxclYKAVITg9p7dh52lNBSs5yWra77MJ5/FTimI=","aunts":["bvB/IyNsdtU7RXBexyehw+Q2TGHxJAPYXBR+6lDmhvs=","wq5R+bTkgHH2KMWIQu61uVGfgxiGIcJ+NCyVHWgfHqE=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":99,"end_row":111},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6QvVuGXa2RMRszSxRLGYfXioOzNFPXq+q/c0ptuu2zc"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8kGGejdIDNAbMz600F/Mr7/d0m42Yzj/8MVuQVO1P4n"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xBoFfGwXh2EhyejEwq5KfWax/LMjoVDTvf2YwhhaUHS"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+Pey5g0o/3bRNaTs0272SWm3Tx0toiMweizmHnEqVeq"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2VYTRlyVxv11AwdhqsZxe/cJgBxrVubb5BGFVYSSXdz"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wxDVDyYiBpjizK1zmBG6JllMdYzQzMACgTTvLqe60fk"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xr76OkC+B0u96yPTHdjn1TlaVz0hLCWkXbmX5fNG8ni"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6X7VTF1tniyWgO0NI4tdIE1CskUbG9tnz6q70xWb1WE"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3QdRJjhvMApRr+6YBLr6adJVK2XDGg3vdy8gaNCeb12"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3d6P+TVefscPa5wIAVVNTLaMxDoTgWXBcX2O2peVhFO"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2+IQuFJDs7DlDHBICdJ/zCr1XYeclDIcUj+eBFgTaPh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wP4wfKnJNSieTFslO9K8cyP3Ic84zdFn0mgrDqCwq+O"],"is_max_namespace_ignored":true},{"end":29,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJKkj/q2BG/p5ItDnQxZkZChmI6SOwmke7maj/vxrFor1","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJFX9my5t8+w8CJADYZxXnIbGaTqliMflKr1Gt0aWGivF","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M1w/csEfKazrcsUaqGaAqVCGvP8FXDjihNF10oiZuzOJ","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MwAPg9SrSSlKIn1I/auROIJobGRoPjt36aTVToI46BIu","/////////////////////////////////////////////////////////////////////////////8kk8Gqbi+yE3sPauA988PQ2hY8JMoDrm+RNTTclbXOW"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJDqJU5iBOe435h9UIGv80ylQz4ygl4HIiz5gOFyvqXly","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJJAaz/b68Eur1yENb+9kGE1z/T9lNLQjT65HzoNMAJb9","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJBSk6Ay3n+Wd+RvKUGlCdb0dcYeYtNfkn3Qzws6cB+93","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJLhIt5NpVq8g5rD+BNIGrFz5engAxc7MMBhR63CFmHpR","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJIuiB/ktqzuVkQku0xrIJGW8Fl96qfdGbrw+ndai2Hta","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJLkreiuAVLSI2aGiy2KatU7vobVjAqDQvQ+SDEl2tiRe","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJA3Cf7ss2SiaperjLHIUGelgXk8y59nqGTUtebNdTXCt","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJFWC7oMF11nVfVOiemU8xpOR/CbxbNAIe0Ry0QBoNFbl","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJD+bOta/DEmL0KeUUqPh44kaR4NXBps9SxhBEwy1y8qD","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJAMPpaEA+Mb0EkXtXvy5XwSeq6F0OBBdIjJEiaI6TVSI","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJG8huxN10q1Mpvk0jSMQvxHekrkV//lJESfuwvGV3/kI","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJJ3UFv79yIa54IN/PxpLk3BHyaTrhudMliLEk/RPZEig","AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MxqhQlHwx6G2tCz73LzgB7Fe3l85116D9Y2ytzCPR6nq"],"proofs":[{"total":512,"index":99,"leaf_hash":"mOSNHBdiJ/M9gS6YIItj9EOdjuJdbco0brtkF/ZN6Rk=","aunts":["1q55h49DWuLAx633Byb5EiV39vm+LOYTohtaugeN0sg=","eX4pk5V+1XNpOVhsP8MaoZvan9eQ2A4cTi/1xeHjzQ8=","BPM/cK3EjsNrihku8TTwgcKFuAcbNv7T062x62fdEiA=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":100,"leaf_hash":"V+8rW8z9SRrM6rxr5w8I6VOrF4pRVaiyvYT5VrPAh8c=","aunts":["Kqg95hzXdob5ByTSRJkhJOwMrIK6NFUlioqZ6UuiXjQ=","FodCi6Qvtr92FygebPwvbI+hI1j+78SlLS6ouuIlOz4=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":101,"leaf_hash":"Kqg95hzXdob5ByTSRJkhJOwMrIK6NFUlioqZ6UuiXjQ=","aunts":["V+8rW8z9SRrM6rxr5w8I6VOrF4pRVaiyvYT5VrPAh8c=","FodCi6Qvtr92FygebPwvbI+hI1j+78SlLS6ouuIlOz4=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":102,"leaf_hash":"0lrE1glNSdD62EcKeEF7OzUPuehaPhhLyQLtczmVKpk=","aunts":["IsxW2PDXa87CS25GRv9vgih7BTotz0gbWkA/FwO4ADI=","xjS1mobRlxcPLVZ0oP+4O5TA1ehbCkUkrdRCSXw/f90=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":103,"leaf_hash":"IsxW2PDXa87CS25GRv9vgih7BTotz0gbWkA/FwO4ADI=","aunts":["0lrE1glNSdD62EcKeEF7OzUPuehaPhhLyQLtczmVKpk=","xjS1mobRlxcPLVZ0oP+4O5TA1ehbCkUkrdRCSXw/f90=","4aISYy3WQbSNwB7WYkl5eDtbvzj62NkxyiGu/osAdPw=","XvMInEh+hUHtVnuUrO/JdqRe0aMQh51WlkJ6nwH8CQM=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":104,"leaf_hash":"yIN812UKJWUFudwFKjEpJsngLDw1jz9W8s3hHxsrhmE=","aunts":["Jqz2CC+PyImkPxu/EGRp8pq8uBTGZ1g79gqYLYfkMfo=","V9rTIsdMdcq+SiMqNse7z6xEtkjCGLYPB2jAuPM49oA=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":105,"leaf_hash":"Jqz2CC+PyImkPxu/EGRp8pq8uBTGZ1g79gqYLYfkMfo=","aunts":["yIN812UKJWUFudwFKjEpJsngLDw1jz9W8s3hHxsrhmE=","V9rTIsdMdcq+SiMqNse7z6xEtkjCGLYPB2jAuPM49oA=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":106,"leaf_hash":"6tNm2Mb6K117jRBNmPMJh/lRVWntKB8j2/vhUwhqOwA=","aunts":["B//Vf13XpnPAb5ILZ1JFC/3X/NzELsuaRci7c6nlCrQ=","1SdHCpTre0I49VkZPTjXeB7sK/wKNrHpkR7Kv0Rnylk=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":107,"leaf_hash":"B//Vf13XpnPAb5ILZ1JFC/3X/NzELsuaRci7c6nlCrQ=","aunts":["6tNm2Mb6K117jRBNmPMJh/lRVWntKB8j2/vhUwhqOwA=","1SdHCpTre0I49VkZPTjXeB7sK/wKNrHpkR7Kv0Rnylk=","bbuWvmi9lzpEKgC0fkgWAl6zt8qmesCb+96W4a/oij0=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":108,"leaf_hash":"P1o9UzUZb2chjKzfkjgjN3b6D3cpKeMJRZhyRUXOj8Y=","aunts":["qY7DjvfAdATYAUf3OaysC92TZf8E3ud9l210+Y3iGgU=","ow+/vaWMmrqRFYx1qCIRF1ZND2oFTRTMW51xiNDCOQk=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":109,"leaf_hash":"qY7DjvfAdATYAUf3OaysC92TZf8E3ud9l210+Y3iGgU=","aunts":["P1o9UzUZb2chjKzfkjgjN3b6D3cpKeMJRZhyRUXOj8Y=","ow+/vaWMmrqRFYx1qCIRF1ZND2oFTRTMW51xiNDCOQk=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":110,"leaf_hash":"bvB/IyNsdtU7RXBexyehw+Q2TGHxJAPYXBR+6lDmhvs=","aunts":["lon6nxclYKAVITg9p7dh52lNBSs5yWra77MJ5/FTimI=","wq5R+bTkgHH2KMWIQu61uVGfgxiGIcJ+NCyVHWgfHqE=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":111,"leaf_hash":"lon6nxclYKAVITg9p7dh52lNBSs5yWra77MJ5/FTimI=","aunts":["bvB/IyNsdtU7RXBexyehw+Q2TGHxJAPYXBR+6lDmhvs=","wq5R+bTkgHH2KMWIQu61uVGfgxiGIcJ+NCyVHWgfHqE=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":99,"end_row":111},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNIpjyGRuaRJJkRGt9bAbFZ7wYR/DGxgtcwzcYM92g7Rd","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNI0RblBYFy9Whm2hiAJ2kJYzr4K2zOMueSupLt6tsLKR","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNLBHTz2OhCQ8qopt7x46rwZx9TFtjUYJ/BfU/l2AKwI2","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOQhKeGRsAMTitquhZQ2THGMuQDi3Fm+EfW8s2gQmPLX","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNA/31LHaGb0EUAyVaB22gRLnyPWf1KIzqCFCvt4vlFhg","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNJOGijLgUPjYOzbGq9XS3uR8vLDVxre+Q+X5ll/zK0re","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNIDMyYmnOF9t8JTvSlbT1YpcFw2ARFNIvDIBZTqXvrZE","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNEBZVI7iM4puMBrU3NISyb5IMxYIH0iRop9SqMTVf7Qp","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNF1iD4x07KffXDOWD6gT+f59X8SIzybC4kRsuwXN2P9e","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNHCHu0WE5T/VHLWP8G9O2Ko18QF0sFiurJny1tVWDdw5","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNN3mASiDWIKMlTK/3HOqgA3XJrqtMpezt0Nz8xo7GTLj","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNIFfa7UQoNjoJm5AtcHeMFIPfTAcyTV7T7QjOziUZL80","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNBmG6nZJgfQjHbCpJ9ZvubFX3T271AviDdiKqjcKtKWC","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNMNhHicP0U42N8si7rvhe3PiADwQEZTssln4mHmd9xt1","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNCcrFqDxFE8lu9+ZfczAcNoSnC12ebgwVVcXa1utt2eK","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNBtcDgHLgq/cCJGQ+rIWviv5rDPPEklAo+FXMD7G5Wwe","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNLu/aMpnrtEUGDG9QRiBn0K+reuu9zj/26059qsbKkhu","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNDUjKvoe4ocfFES1spFFEXXE47EvCmXQJONvrHsBb3Ww","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNPgT3bMEMDPCLzFker2IMGFKSmJdn0G7j0frBxrxW36O","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNBSe+C3gTOzi1Qr+QSjzz2AEm17pG03CcGdwESrAiAyJ","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNLyYDIWZK2jcGQ3PCmTfch1L0LK4+tBen7TsiRAaCKHg","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNPBdFeaGFhMsrKTOmbmFzWfMIhuy2MWh9Oc/TRs4U1mq","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNGc0w8APfx9HN8Gp1cyuo8OCuJwGI3pFoDrUaECqhocX","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNBmOBWiRxcgdBFFLL1gRdbNnceha3uV7aD0vUFwVEsf2","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNEoTJ5d2vBeWVW8nwOG2Hl4NJ5rtbkH2Qx1/O6W5zxGe","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNPhv9kkvN7QAoWbiUc745iWpHBOqjUeNQ9BqW05UmQeT","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNEWc/e27i51bkjmSTQQmbUGVlp5NRNz4J/Gwv1McwH+8","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNEjA2Ptoe81ypcrmWZe8RjPnGHNC7TvWjFRLhVm8cuPM","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNHRFgukp7BBiWNHRXoOXm7xyZiupqDN0Q1CKgmOpDP85","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNAzHDVQFM6V7wuQZ0ql8/UU9ckv2ME0wSE5Sfo1aikmn","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNK1f89oC9Wm1J62jK0rWlADI+dgV3pxJI6W7EuaYeJj3","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNB29mF3HQsuoipHIfmK8hqn5mX/L1rqwAZhckSZQopH3","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNAqKUedUprgHVJjxi3S0N0+ufRikACrRfdarR4g6bFBX","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCND77oxzMORafFpR71kj5kf2Px7kSisDKsYbZVoE8aQzt","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNCaHrxMansHhXm0pMJbzbWzNqsscvJ1cKpdmUXAmobDm","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOfDw89MzEXiBP+S8SkCRLuUVBlv69c14+MvrPAkxTZg","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNGwBnNiI9b95DeeoFuKFCyHhCYQiC2uTJYqzZlaVNhDO","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNNjYB4WABjgjnORu3wf2InzZhaJ7rzpmN+4gI8Ou9Pm9","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNFqNFJ/vSXsFYNceNUPRzSJADEOQFKF8hMhyMEVLzAQa","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNJbUB0aupJy79ScEGO7ODnkhyqazcYOadK3fGBrI4js4","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNFrxnE/qOuzOKZyPxpQUkTkqvKMicQ0mPhnh53YgHlwM","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOo/ieVWsKkHh9z/GUbwN362/VAS6kJ0FP3a6C/HcXEV","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCND8bcINIgkwO0OXJHj9WZ4HsemW/10+tZuQi7NojKmeq","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOkZuIweo+k60BJY+F+qenqJmw/bX6V5bs489POPuGNj","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNMNQ6qqb8NLLmin6qFElAlsVOEh+2g87yjYHaC37E8eD","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOvbNyolkfToZlkvVidg6Ikps5AOKF6bbHukmmpACDs/","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNGUaPpurq/vtB6DzQzhBbTwWZj1nSoRStH5HdjXacDiZ","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNBM9XhewliECVcSw/rTe/YW0kH0MYBBpoMhQT87eiEU4","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNJ0o69OIbgmeh4+X5nX2Dl8EXxQ7SmUajspvLybTdvDy","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNOSEAoiMOu0nYHnbkyRrsXBRZaVpp4159VkZ+iGWNlo2","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNKFOm6un7yrR3Sjt/wfRAsTdGLntKg/3d5GNS8UO4P4e","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNCcVGuSw0UzF9BznXGWgK6Eei5X+pouYK9BmKgma9Uuq","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNM+OV7aWtKFJXfdWxK5cODMm8lzU6Ch6/lwlrw4W8Lto"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//vby3XM7f+Kbqk7xdH5VBLrHt1y7DMgADgUaIghR3Uu","/////////////////////////////////////////////////////////////////////////////8jK61yWgVWmT18/34v2A4P3X7Oxt8j90wvNkcpyTtbt"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9pBt6H9o8maD0tIKwqVRYxw9ftJ77FL5Nb6604lKLOa"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1SnBDs6OmsnUeFN6Inbn71G2JygA1Q/X4qJDDK7pVnM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wBh6OYczuGEHaQfB+tAC6zj2QNCA2oZqeZrNmdUWX7Y"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////5O7b58yTGOcbNFLUx+3DjDAYn6E4qVwiowU4PFy5B2m"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8KBYnm0GdC9FUpvAXCrbSyXc2x9CdZ0iTW9yz6OVB75"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+pNP/6k3CgTY/26zc/LsBopYbv/vEFOSmXarFdv0ctH"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yrnCwsWLnJMcfIOVJdl9s/dusHWjvkZpMUM//5AF/5J"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6wyLUxaDLidEoXIgq9ptwfd4HaCqmqztzof9dKwikii"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zccxk3qsZMiF5lS9iclCeMj3oemCBguavkp6LJvL3I3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////4aeMqGdwfF6dQHuWc3b3Vuis/zNJDoyKc3PypuLanz3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9oPZIf34dDeqb9kbprirFHTRWh+UqgspvW+G+Vu5+qM"],"is_max_namespace_ignored":true},{"end":63,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNHoQGRTc+kY28dmau2X7dfg4Mi2U+nZNeO6CsD7BqeUw","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314lkX4F5uVs39kyo6rUS72VaSnY19f3yVvGg/AoAAKHrH","//////////////////////////////////////////////////////////////////////////////3h5tvoml946LbF1tuJtMITVtdHwAnUPB4rQYbfnlzj"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNNKkLRNWj2TdBLTDrCacW9calCIM7gFyOn2Vjn5AJIvR","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNO7gFjxGEJS469qmujQlUWaKRg1HmLJUFea8vPQibQbe","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNBohJ9R7eTiuzEz9OR0U/1TlAwDmugST8EIsTppL8bjB","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNF5BJR0Es8uWztv61w76BmwxxtClG3DA95MQykp/8oR4","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNJgv2yt8IXCowgISh+XQxy2YpGEpUkTicIsYiA7K+U3t","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNA2iTda7m4s97hkc5Vsy6339AQqyICGXuCUl/YAxshGb","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNDNLQIt1r6W4FMYhUyo6hEhBhziThF2wqNh639idksaf","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNLuHha3KL3aIZz/9J1Mpu2SAEb0QHuwSSm49unZmVmpL","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNCGJI+jpKvWIO+PUL52YQ3jNdA1uhoyeHs1qR1VBqily","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNFP2Kc/YnFS9MPVfwmuFlWHZCaIFZWz0H9OEljeHQ8Wj","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNEdIrzt+38WV8bW4YVR53Z6weZtdjxRzTrhM5VTmuhE5","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNJNZQaSkO44UaqbCkxNiw8gNXJMreCZU4QLMQPgQvRWf","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314mjwKPyTs9huTlUTWlKcsLrr+/LIS86LrGnMHfuPKUvO"],"proofs":[{"total":512,"leaf_hash":"Ly3cfkZm/8hKdfmpYeJUifs6RAR884sOBS3BpOpjV/o=","aunts":["tcori6FJM+b+K7qr/PerKnZy4Z3OV3YGhDgTsjTATvY=","3Z/iiAVkxQcXNt4IJPIeIyO9B6kjbAo4zdO92HxxeiY=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":1,"leaf_hash":"tcori6FJM+b+K7qr/PerKnZy4Z3OV3YGhDgTsjTATvY=","aunts":["Ly3cfkZm/8hKdfmpYeJUifs6RAR884sOBS3BpOpjV/o=","3Z/iiAVkxQcXNt4IJPIeIyO9B6kjbAo4zdO92HxxeiY=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":2,"leaf_hash":"8g29shgHWLvkpjwIEbtFctrWEqfhXT5oleCY09rbNzI=","aunts":["5ibjRT94kU/xiF+lMV/JRwTQLauL8BVbMGd7afEGCvo=","Pki/yagIKnUZd+Ko/xgwegsPdUg09aPAc165VXLVm84=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":3,"leaf_hash":"5ibjRT94kU/xiF+lMV/JRwTQLauL8BVbMGd7afEGCvo=","aunts":["8g29shgHWLvkpjwIEbtFctrWEqfhXT5oleCY09rbNzI=","Pki/yagIKnUZd+Ko/xgwegsPdUg09aPAc165VXLVm84=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":4,"leaf_hash":"HNS1ULaYuXZvhkUXeq5dYnAHmNWfyEDhvdjPrYcWZgA=","aunts":["GbpjiD3TKssRMsOwK497ywKqdev1XoIEGOi/xha2ydc=","TT//rDsnv+JzFuojvuneiuTHZNzrXRaqvR1WV2jeNPg=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":5,"leaf_hash":"GbpjiD3TKssRMsOwK497ywKqdev1XoIEGOi/xha2ydc=","aunts":["HNS1ULaYuXZvhkUXeq5dYnAHmNWfyEDhvdjPrYcWZgA=","TT//rDsnv+JzFuojvuneiuTHZNzrXRaqvR1WV2jeNPg=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":6,"leaf_hash":"b0ftuDbGDa6m0R4ipUA2BQWi9S6SBX2pB3d+0WgJUF8=","aunts":["LiukzrVG0fi9Un/fjEIpknmkiW0RUL5akN1SMLbJ+pw=","mT9rTqzpLdlQl4jgRXuEojFPbtDYIUxzW7hAnvEyGJw=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":7,"leaf_hash":"LiukzrVG0fi9Un/fjEIpknmkiW0RUL5akN1SMLbJ+pw=","aunts":["b0ftuDbGDa6m0R4ipUA2BQWi9S6SBX2pB3d+0WgJUF8=","mT9rTqzpLdlQl4jgRXuEojFPbtDYIUxzW7hAnvEyGJw=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":8,"leaf_hash":"yagHWhnOp+GRJIV/PVQGn/gl1ANkl3B99vP3k3Im53g=","aunts":["2vmZE5UVmlwtE+EznQhdjKFDjG407K+dkV6uczFQBjs=","v9IIP0BlPW2U0d5bT8gwfKEZnaFEOLOiMTrYcUylOYE=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":9,"leaf_hash":"2vmZE5UVmlwtE+EznQhdjKFDjG407K+dkV6uczFQBjs=","aunts":["yagHWhnOp+GRJIV/PVQGn/gl1ANkl3B99vP3k3Im53g=","v9IIP0BlPW2U0d5bT8gwfKEZnaFEOLOiMTrYcUylOYE=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":10,"leaf_hash":"cjHk7+ZUB69wRrswbISBFiFuRyDitwLPS3DoV2MKCMI=","aunts":["Auj0D6WRx6jVV5HDumm0GKfPig8BdxeyQZfcosjpKfY=","BRyeGL481EZ8Z4OHtMgYG5H38HlU08Ywf8iDBrZlWik=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":11,"leaf_hash":"Auj0D6WRx6jVV5HDumm0GKfPig8BdxeyQZfcosjpKfY=","aunts":["cjHk7+ZUB69wRrswbISBFiFuRyDitwLPS3DoV2MKCMI=","BRyeGL481EZ8Z4OHtMgYG5H38HlU08Ywf8iDBrZlWik=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":12,"leaf_hash":"0B5qRFQ4zr5aArC+LA8dhvY5DgJ9qELdo4gJ8SFwqow=","aunts":["k6rFtu9eHe2Lu3cwSUylBTOL8pCWUeJPI3Eh5u6y8YA=","VFk3nrGeCwpnY8sdiv9RvLrglQjbDPVeZK7DcmOkfwA=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"end_row":12},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//vby3XM7f+Kbqk7xdH5VBLrHt1y7DMgADgUaIghR3Uu","/////////////////////////////////////////////////////////////////////////////8jK61yWgVWmT18/34v2A4P3X7Oxt8j90wvNkcpyTtbt"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9pBt6H9o8maD0tIKwqVRYxw9ftJ77FL5Nb6604lKLOa"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1SnBDs6OmsnUeFN6Inbn71G2JygA1Q/X4qJDDK7pVnM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wBh6OYczuGEHaQfB+tAC6zj2QNCA2oZqeZrNmdUWX7Y"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////5O7b58yTGOcbNFLUx+3DjDAYn6E4qVwiowU4PFy5B2m"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8KBYnm0GdC9FUpvAXCrbSyXc2x9CdZ0iTW9yz6OVB75"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+pNP/6k3CgTY/26zc/LsBopYbv/vEFOSmXarFdv0ctH"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yrnCwsWLnJMcfIOVJdl9s/dusHWjvkZpMUM//5AF/5J"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6wyLUxaDLidEoXIgq9ptwfd4HaCqmqztzof9dKwikii"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zccxk3qsZMiF5lS9iclCeMj3oemCBguavkp6LJvL3I3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////4aeMqGdwfF6dQHuWc3b3Vuis/zNJDoyKc3PypuLanz3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////9oPZIf34dDeqb9kbprirFHTRWh+UqgspvW+G+Vu5+qM"],"is_max_namespace_ignored":true},{"end":63,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNHoQGRTc+kY28dmau2X7dfg4Mi2U+nZNeO6CsD7BqeUw","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314lkX4F5uVs39kyo6rUS72VaSnY19f3yVvGg/AoAAKHrH","//////////////////////////////////////////////////////////////////////////////3h5tvoml946LbF1tuJtMITVtdHwAnUPB4rQYbfnlzj"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNA==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNNKkLRNWj2TdBLTDrCacW9calCIM7gFyOn2Vjn5AJIvR","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNO7gFjxGEJS469qmujQlUWaKRg1HmLJUFea8vPQibQbe","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNBohJ9R7eTiuzEz9OR0U/1TlAwDmugST8EIsTppL8bjB","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNF5BJR0Es8uWztv61w76BmwxxtClG3DA95MQykp/8oR4","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNJgv2yt8IXCowgISh+XQxy2YpGEpUkTicIsYiA7K+U3t","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNA2iTda7m4s97hkc5Vsy6339AQqyICGXuCUl/YAxshGb","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNDNLQIt1r6W4FMYhUyo6hEhBhziThF2wqNh639idksaf","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNLuHha3KL3aIZz/9J1Mpu2SAEb0QHuwSSm49unZmVmpL","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNCGJI+jpKvWIO+PUL52YQ3jNdA1uhoyeHs1qR1VBqily","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNFP2Kc/YnFS9MPVfwmuFlWHZCaIFZWz0H9OEljeHQ8Wj","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNEdIrzt+38WV8bW4YVR53Z6weZtdjxRzTrhM5VTmuhE5","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAJrlhZ6SeD+fCNJNZQaSkO44UaqbCkxNiw8gNXJMreCZU4QLMQPgQvRWf","AAAAAAAAAAAAAAAAAAAAAAAAACa5YWekng/nwjQAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314mjwKPyTs9huTlUTWlKcsLrr+/LIS86LrGnMHfuPKUvO"],"proofs":[{"total":512,"leaf_hash":"Ly3cfkZm/8hKdfmpYeJUifs6RAR884sOBS3BpOpjV/o=","aunts":["tcori6FJM+b+K7qr/PerKnZy4Z3OV3YGhDgTsjTATvY=","3Z/iiAVkxQcXNt4IJPIeIyO9B6kjbAo4zdO92HxxeiY=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":1,"leaf_hash":"tcori6FJM+b+K7qr/PerKnZy4Z3OV3YGhDgTsjTATvY=","aunts":["Ly3cfkZm/8hKdfmpYeJUifs6RAR884sOBS3BpOpjV/o=","3Z/iiAVkxQcXNt4IJPIeIyO9B6kjbAo4zdO92HxxeiY=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":2,"leaf_hash":"8g29shgHWLvkpjwIEbtFctrWEqfhXT5oleCY09rbNzI=","aunts":["5ibjRT94kU/xiF+lMV/JRwTQLauL8BVbMGd7afEGCvo=","Pki/yagIKnUZd+Ko/xgwegsPdUg09aPAc165VXLVm84=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":3,"leaf_hash":"5ibjRT94kU/xiF+lMV/JRwTQLauL8BVbMGd7afEGCvo=","aunts":["8g29shgHWLvkpjwIEbtFctrWEqfhXT5oleCY09rbNzI=","Pki/yagIKnUZd+Ko/xgwegsPdUg09aPAc165VXLVm84=","q3EiufNvfp1Naq3BfIR96E7it51AIxZoiZybeenPyg8=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":4,"leaf_hash":"HNS1ULaYuXZvhkUXeq5dYnAHmNWfyEDhvdjPrYcWZgA=","aunts":["GbpjiD3TKssRMsOwK497ywKqdev1XoIEGOi/xha2ydc=","TT//rDsnv+JzFuojvuneiuTHZNzrXRaqvR1WV2jeNPg=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":5,"leaf_hash":"GbpjiD3TKssRMsOwK497ywKqdev1XoIEGOi/xha2ydc=","aunts":["HNS1ULaYuXZvhkUXeq5dYnAHmNWfyEDhvdjPrYcWZgA=","TT//rDsnv+JzFuojvuneiuTHZNzrXRaqvR1WV2jeNPg=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":6,"leaf_hash":"b0ftuDbGDa6m0R4ipUA2BQWi9S6SBX2pB3d+0WgJUF8=","aunts":["LiukzrVG0fi9Un/fjEIpknmkiW0RUL5akN1SMLbJ+pw=","mT9rTqzpLdlQl4jgRXuEojFPbtDYIUxzW7hAnvEyGJw=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":7,"leaf_hash":"LiukzrVG0fi9Un/fjEIpknmkiW0RUL5akN1SMLbJ+pw=","aunts":["b0ftuDbGDa6m0R4ipUA2BQWi9S6SBX2pB3d+0WgJUF8=","mT9rTqzpLdlQl4jgRXuEojFPbtDYIUxzW7hAnvEyGJw=","+PXGIlGbSHQGT1FTB8jeGBXIX/bs/cVDt0auhQu3YXs=","OdOeAHo/DJE0uQql8uk19tsfa/H1uzBeh2KRfrze+CM=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":8,"leaf_hash":"yagHWhnOp+GRJIV/PVQGn/gl1ANkl3B99vP3k3Im53g=","aunts":["2vmZE5UVmlwtE+EznQhdjKFDjG407K+dkV6uczFQBjs=","v9IIP0BlPW2U0d5bT8gwfKEZnaFEOLOiMTrYcUylOYE=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":9,"leaf_hash":"2vmZE5UVmlwtE+EznQhdjKFDjG407K+dkV6uczFQBjs=","aunts":["yagHWhnOp+GRJIV/PVQGn/gl1ANkl3B99vP3k3Im53g=","v9IIP0BlPW2U0d5bT8gwfKEZnaFEOLOiMTrYcUylOYE=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":10,"leaf_hash":"cjHk7+ZUB69wRrswbISBFiFuRyDitwLPS3DoV2MKCMI=","aunts":["Auj0D6WRx6jVV5HDumm0GKfPig8BdxeyQZfcosjpKfY=","BRyeGL481EZ8Z4OHtMgYG5H38HlU08Ywf8iDBrZlWik=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":11,"leaf_hash":"Auj0D6WRx6jVV5HDumm0GKfPig8BdxeyQZfcosjpKfY=","aunts":["cjHk7+ZUB69wRrswbISBFiFuRyDitwLPS3DoV2MKCMI=","BRyeGL481EZ8Z4OHtMgYG5H38HlU08Ywf8iDBrZlWik=","JypCjItuG+PpX8VBaHuK0QuCJxbsIRfkoGSEhsAqeYw=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":12,"leaf_hash":"0B5qRFQ4zr5aArC+LA8dhvY5DgJ9qELdo4gJ8SFwqow=","aunts":["k6rFtu9eHe2Lu3cwSUylBTOL8pCWUeJPI3Eh5u6y8YA=","VFk3nrGeCwpnY8sdiv9RvLrglQjbDPVeZK7DcmOkfwA=","ulIb0QRQ0XjJii2QePs3wAg+nGDDCu572pxoXIEoOVI=","v5KBhCpLdt9dnuoQlvYa0eUDlfAnTL8bCNljvUFhXyI=","IJ6AdJcu2BhsOVv+gkmAn8p0lkUIBzWk/4ZEnRIzBLk=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"end_row":12},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0K1lIHmfVxoWvPeefgtIw58Vice4Z7yS1jRK5EqeQtN","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc1vZVQe9wPWatMITBKfM9qzttgizzChftd0R83rLKwfL","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc43n0b3aQK/rt0b4oISGOomAHPhtPBRqiqcDkBU98V5G","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc1sLX94xf9xPU6y+hC0PxxP2CwOi5CPcGZ/ouyKDyFsk","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc+hfz2juxn+FGcWuwG23mK8KR3dhmGXTSpvyYsxgZLb+","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc9gLH2/wE3Y5yHKUBgAc9RvchzzL8l8Jw++obEd1v+gF","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3q9MoJvgkrE8apKGqHrWD2YMCwtkRcRHziJJdGiM9Vk","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcwzShTP0mFKitI3QKsLc9CYIGVoNRtD0NzT2tfkBcQz0","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc6orcCjY4rJgT4uDwj+cqDtkL8vK1twX5VhDCgWZbefR","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc76pquU42terviESS3HKFdf3dLAiu5E95LzrsXwzTziU","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0ZR7+XvrAvPY9vue/z9G8xZY+vAyDEZ9VKHjYBw5yX/","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcz6iSN3wDf1M2FiGFZ8LaGhZV80OadtpFByMtz+HTFxT","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcyvCVhCeCN31pgsDKq3y8FzQ8bsBJ46CYFumN/to9is8","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc+2vSZL5f8dyOjH08WVNI3yUyOLIZfT8aBFnwTxDDiuS","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0lKQt17vFArPbe+CvzbDQYJDkNxGmM1Ti+Yizt4TWZK","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcwmURb2yia02NaOgXK0cK0AsCrobwhdRinZY9Tuqv03z","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxSIJos6BmZFZ8gZvlgv4J5LHGYB5EgW8iV34hOLQb9u","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc+YA+PaQr9p7UDoT+5QY64HUUDIwhZ12K67Qwku+3ab8","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcyzcOLeQHjwje3yLg4OH0Ti4eeB8tC+OU2y0Vx95h40T","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc7xD/g4XN7ceLf1gt+UhY9w7rUHwVdnugkTdftDyXqH1","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc7P3iuLn8bihBXXNzPGwZ1wlLMkqoiNHWylZZVlcZvwP","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUczUAQEX/7QUTRdlOvq+NbFy20CUfCBoWVzHnKKea0PDa","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc/zD+PJWK8/UW55WRPPIw8Xn+A+V2MjSe1XvwCAyrKjK","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc6/D9LCRqESxDAZ4QHkL4TxdUzg6WK7eY06zkCDMDXCq","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc1WLrYtg6Dlz4bSZnAfVJJB0DBREuKqjXz0g48PxmTHN","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc6TFJwyT4L/oudXm6k5TtViXWgGS1lhl9JRnStdUpIJL","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUczF7vy1SC0QK5HofATScHiz3L08yGNnnOmuQTHbEKhwT","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0Ol0Pjl2YpRPmRGggZMtyAx66Eu9X1u8LPimCNBZqqc","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc+OeikjNGwbA/IockleFlemE8Tygt0cYkg9HePySoW4J","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc66HQX1nXQGEI2t5Lc6IKutkdhsmmeDkkOw3wZUU+6Ih","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcwPauhw1tqQbw2XLAns36S1YOZdvRMMr6tJZZFECkdYu","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc8EICuxeIBULyH1ErrjYIr72d4SyaKVhul33JLbfzcMm","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc/IMxL1shbvlCbBf+d5sB1SWHG9Zt6l1OR3MxjgMYsvb","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcwMOeVQ5lvnpX9lurJ1Sy25MRj90uDeTULgmTz7Giwh8","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcwcYaYiY6P5BNuWkWubViwJoybT+kY7xvdq7U3jJgdLD","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc6t2xPS/u/l+MnvcF1gXOLBpWwg5oSh7hXCTmjOIz8wv","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc+fZ78pSiZ+IOIWgH8DZKdyvRxwY22Nu9pmExEUpObDc","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc/CMAIWDKtBhpvNy07RhA1+R8X+pZnpABcAc5PGEMFJY","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0PpJTSoY7n1SkaM1a0YNyBzL6Jzetmof0IzNyyCwnlF","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc76qFF/fEL0pbpDxHj3UfnQ/SCcu2GZX7XgOWAwHohDY","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcwKXC8OOz1ilfYXE2yx7v7tSYt4Vw5A33boTEYOm2OvC","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc9o08Qz3GFeu5f2NwuSEGZXWE7oPBvB310e4ZWnjLTnv","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxy+tsqK54Fgoq2Zks618K7xp3NGVyoIxBs2LjjFtovd","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc/blM+Ydjmofy3Qj0lA/MsA5q9lg+m52kg5AY6ZDBdNJ","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3Mn3CDJXlU+2KkWDWyUk3nkZXLXL6pFy3Aq62opisGU","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc4M3wZHNj6Eje8M0LjAJv8nIu/KHlCctd3CpzzVYptRJ","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcyBNH4kkd2D27HKri06oc+X0lhlYa+Dyv+iunwWvZTRD","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc1Ux2zUSjyu9K2rxsm6IJkbUVLo/pk9cv2+A2BJylx09","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc69TDhrMyq6UJNFQtMhkXn5MWE6n16yd8L1rKlkZJCCX","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxT2YngRe8clByWTY0iRNND7f5QJ+EZ0cuoCirpWi265"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMkGwXzk/bcASJcFU+WLhwosT6gG6qHpY2xfQ1j/FxJAv","/////////////////////////////////////////////////////////////////////////////yKtOfSCDhJXTtE/jqR6axp5wfNWyCcXeIJwzfj55qhB"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+hTWl8hPyH9wzEzZ/5+i2xj1+r3Oqu7NUWqNw29fJF9"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////y09y6XvNhpsH2bukbElQZsfE+zaskuQqQcT2FQ/QHaq"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0KYNENNRximR/rvNshJc7pEyEHaamQHhb9a9htIulPh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8YdvDwCXSAXO1114UMBIzNmboFWoNF3prRttJcoOeer"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0e7WJFrfEA5TaRTwn3yPnTXSZhjgnqEzolOh9CpW/nl"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////ygMmUjCkyCLetdkqBgCPebw6MnGHDdOaJo//6wfcnLx"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8/HNMRsWGmoOevdjxsyDaVOijUs6ex+p0CXuCesMfcM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1OvzOurcgjlOD+fTll593OmaCbracQf2ozS5L/ENok/"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2v2+DAZ/eRd67uIFVSYskPjZJIdJJRqAItsaQHriyE3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3Hs0xhhqmFu3xWwqAwg2ShkKk5XhHn9oSjXxsZKXNtb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1/59W061EZ3kSM9jewniCfFy1m+1KgI3O4ngdwLL/jf"],"is_max_namespace_ignored":true},{"end":65,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxDHcYuZWBD+laU++Hcp02wzS/BzVYGK3sYI0yr/JD/+","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcyd6PB2EuWGErncE1mjXQhkeCvuOYp3v99SVJuFWoO1P","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc55i10Gwdbd8n3oLxIzOh8YF+/R687x+fTFHaCXQ/byc","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUczhYKG6ruAyVhIZhfDDHjXkrYhzTCwDU7KKGkSuHbLPG","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc64y7NGfknMmcil30U29JM1RSkaIRVerqo/mBo+ePA3h","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z2JV+lyEBPwrRM0DCxTcHm+Zzj+DWjKk6EytXJlfAa9k","/////////////////////////////////////////////////////////////////////////////z9JMtR8d8P8W43+gj639fpzBGCv2viAk/AGgiWmbtS4"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc7C7SEmLT0ttB9wvClOreUyfrC4VXm3l3wFZ1x6/7R8C","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3pznw5XzqCzJijTo71Dqk0QWufsW4vsmb4Gt5K39qGt","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcyZTbaZ9ibCtcdCEZTkn+RQXS88ysqzKEuMZueN6rND1","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc820Z60amIUuCvWxrxdkCy0s7smMi83VU/0YXeqZh1y/","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3bdPBM09awEKrFQG03EqhOuAxz7BMPJiiS2/mCp8jU/","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0HbUiyQ+GKiCuvjC5rN7rDbtnCHaRTicLLYjaUU6WTk","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc2PWRf4DlCRH+pak7eeAvlTXqQZ8dqQaOGbxJ/lb6+Nq","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxINolhohPQPwqI7UjiqUEYJubpP22u3LpFFQaW2JmE9","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3j1g+2rpVwGqe7NQ1ep4pbuR4+TpBJItpyM0IAq9UNq","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc5sN4MBRI7OuEhN/474NkZmhefb2LTcT4LPuzKGI26cY","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc4XRY5SVMjYHZDFQ5Zq4dvBjKFsW0A2jLa9P0qa/8tmI","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxxPR3+Mm4FCbWHWDh1nZfX8/7DiJyJdkV1+Uc0WCl7s","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z3w9Ip5QwpmYfr6GjYYX0fuoqLh52mPV7WCEB+uNSn9C"],"proofs":[{"total":512,"index":74,"leaf_hash":"D3sfh6znFeJYVktarjzn2LoCUbqBKlTqLpReoSoHeh4=","aunts":["wwwOikEBsAd7rtba8sp8TWKhZltNkrhbTnJKbX/ms+w=","mU6FJd54C8957gvYYmaAycmG1MLaj1qpXuPCjO2+Z/U=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":75,"leaf_hash":"wwwOikEBsAd7rtba8sp8TWKhZltNkrhbTnJKbX/ms+w=","aunts":["D3sfh6znFeJYVktarjzn2LoCUbqBKlTqLpReoSoHeh4=","mU6FJd54C8957gvYYmaAycmG1MLaj1qpXuPCjO2+Z/U=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":76,"leaf_hash":"COUX5bHlozDgZ9o9h9jOJFU+jTP4BA5jBgT6iJCR5lo=","aunts":["wsDqk8iJsMOYnI/+9b5EFdORvIxLE8FjeHGV+q5cROc=","PedSgccx7hUIxTLBN87Ia9aK1Psq/7qRqlZpM3FQthk=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":77,"leaf_hash":"wsDqk8iJsMOYnI/+9b5EFdORvIxLE8FjeHGV+q5cROc=","aunts":["COUX5bHlozDgZ9o9h9jOJFU+jTP4BA5jBgT6iJCR5lo=","PedSgccx7hUIxTLBN87Ia9aK1Psq/7qRqlZpM3FQthk=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":78,"leaf_hash":"7RZxsStkPi5AAaanNAjpYW34f8OL4l8GmYjFCnns9q8=","aunts":["8UoJMc4zkOU0KMEnIDDDveCg28DCh6V7L8hL+sEegpk=","4CYNTAvJYC5p3V1MlU99xlp9OTtR9cVFqvUpQk3LLOY=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":79,"leaf_hash":"8UoJMc4zkOU0KMEnIDDDveCg28DCh6V7L8hL+sEegpk=","aunts":["7RZxsStkPi5AAaanNAjpYW34f8OL4l8GmYjFCnns9q8=","4CYNTAvJYC5p3V1MlU99xlp9OTtR9cVFqvUpQk3LLOY=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":80,"leaf_hash":"GXx5ZowNiUJzPW1NTx6O7eO20JivdeJoj5qz9x6BRB4=","aunts":["h8SmsKJGpCoExAZs5eiryH1h8HCjXQYVP1Y2dO17SX0=","2Iv+6tr/xJl2sP5/MwOtIr2J3t2EZAMW1CrvIhyOm6c=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":81,"leaf_hash":"h8SmsKJGpCoExAZs5eiryH1h8HCjXQYVP1Y2dO17SX0=","aunts":["GXx5ZowNiUJzPW1NTx6O7eO20JivdeJoj5qz9x6BRB4=","2Iv+6tr/xJl2sP5/MwOtIr2J3t2EZAMW1CrvIhyOm6c=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":82,"leaf_hash":"MlDvso2902KdiAHE3beuEsLwwtk+1ui5zkxEQ5IKy2w=","aunts":["DUnZo42OM5BjhYMU/ZZ0XdhhFY8QSfu55JvQsctt16g=","eHr4/iJ6pRIvss5IAOLlCxi3GvMgpwGzIxo3k4H7SwI=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":83,"leaf_hash":"DUnZo42OM5BjhYMU/ZZ0XdhhFY8QSfu55JvQsctt16g=","aunts":["MlDvso2902KdiAHE3beuEsLwwtk+1ui5zkxEQ5IKy2w=","eHr4/iJ6pRIvss5IAOLlCxi3GvMgpwGzIxo3k4H7SwI=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":84,"leaf_hash":"yQx/x1F7MLnH4v8whj/A7DTXnLckhLwbPMSJGGOjQeA=","aunts":["pyZYWGZuJ57Hck2aLr+2B6lV/MnjNWSn1oOg3EZ6ZTs=","fCC/SGqvS0jyz0vhWe51MPLxfcJzGTgIuFtJESE0/tY=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":85,"leaf_hash":"pyZYWGZuJ57Hck2aLr+2B6lV/MnjNWSn1oOg3EZ6ZTs=","aunts":["yQx/x1F7MLnH4v8whj/A7DTXnLckhLwbPMSJGGOjQeA=","fCC/SGqvS0jyz0vhWe51MPLxfcJzGTgIuFtJESE0/tY=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":86,"leaf_hash":"OjATVN9Veg7NJFZIRVpu+UY0NuHCQvsBe5OJRKX9OyE=","aunts":["irJQKgIq44t+ynw82tq4EbfmOX+YqEpvcbXqz76HvLc=","Z3gAYFQ+vsI2+cl3q6UqGuerebvPdm5W7dkxrPFsxzU=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":74,"end_row":86},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMkGwXzk/bcASJcFU+WLhwosT6gG6qHpY2xfQ1j/FxJAv","/////////////////////////////////////////////////////////////////////////////yKtOfSCDhJXTtE/jqR6axp5wfNWyCcXeIJwzfj55qhB"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+hTWl8hPyH9wzEzZ/5+i2xj1+r3Oqu7NUWqNw29fJF9"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////y09y6XvNhpsH2bukbElQZsfE+zaskuQqQcT2FQ/QHaq"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0KYNENNRximR/rvNshJc7pEyEHaamQHhb9a9htIulPh"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8YdvDwCXSAXO1114UMBIzNmboFWoNF3prRttJcoOeer"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0e7WJFrfEA5TaRTwn3yPnTXSZhjgnqEzolOh9CpW/nl"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////ygMmUjCkyCLetdkqBgCPebw6MnGHDdOaJo//6wfcnLx"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8/HNMRsWGmoOevdjxsyDaVOijUs6ex+p0CXuCesMfcM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1OvzOurcgjlOD+fTll593OmaCbracQf2ozS5L/ENok/"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////2v2+DAZ/eRd67uIFVSYskPjZJIdJJRqAItsaQHriyE3"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3Hs0xhhqmFu3xWwqAwg2ShkKk5XhHn9oSjXxsZKXNtb"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1/59W061EZ3kSM9jewniCfFy1m+1KgI3O4ngdwLL/jf"],"is_max_namespace_ignored":true},{"end":65,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxDHcYuZWBD+laU++Hcp02wzS/BzVYGK3sYI0yr/JD/+","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcyd6PB2EuWGErncE1mjXQhkeCvuOYp3v99SVJuFWoO1P","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc55i10Gwdbd8n3oLxIzOh8YF+/R687x+fTFHaCXQ/byc","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUczhYKG6ruAyVhIZhfDDHjXkrYhzTCwDU7KKGkSuHbLPG","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc64y7NGfknMmcil30U29JM1RSkaIRVerqo/mBo+ePA3h","AAAAAAAAAAAAAAAAAAAAAAAAAM+DiRBzVpXkv88AAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z2JV+lyEBPwrRM0DCxTcHm+Zzj+DWjKk6EytXJlfAa9k","/////////////////////////////////////////////////////////////////////////////z9JMtR8d8P8W43+gj639fpzBGCv2viAk/AGgiWmbtS4"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc7C7SEmLT0ttB9wvClOreUyfrC4VXm3l3wFZ1x6/7R8C","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3pznw5XzqCzJijTo71Dqk0QWufsW4vsmb4Gt5K39qGt","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcyZTbaZ9ibCtcdCEZTkn+RQXS88ysqzKEuMZueN6rND1","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc820Z60amIUuCvWxrxdkCy0s7smMi83VU/0YXeqZh1y/","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3bdPBM09awEKrFQG03EqhOuAxz7BMPJiiS2/mCp8jU/","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0HbUiyQ+GKiCuvjC5rN7rDbtnCHaRTicLLYjaUU6WTk","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc2PWRf4DlCRH+pak7eeAvlTXqQZ8dqQaOGbxJ/lb6+Nq","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxINolhohPQPwqI7UjiqUEYJubpP22u3LpFFQaW2JmE9","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc3j1g+2rpVwGqe7NQ1ep4pbuR4+TpBJItpyM0IAq9UNq","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc5sN4MBRI7OuEhN/474NkZmhefb2LTcT4LPuzKGI26cY","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc4XRY5SVMjYHZDFQ5Zq4dvBjKFsW0A2jLa9P0qa/8tmI","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUcxxPR3+Mm4FCbWHWDh1nZfX8/7DiJyJdkV1+Uc0WCl7s","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAz4OJEHNWleS/z3w9Ip5QwpmYfr6GjYYX0fuoqLh52mPV7WCEB+uNSn9C"],"proofs":[{"total":512,"index":74,"leaf_hash":"D3sfh6znFeJYVktarjzn2LoCUbqBKlTqLpReoSoHeh4=","aunts":["wwwOikEBsAd7rtba8sp8TWKhZltNkrhbTnJKbX/ms+w=","mU6FJd54C8957gvYYmaAycmG1MLaj1qpXuPCjO2+Z/U=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":75,"leaf_hash":"wwwOikEBsAd7rtba8sp8TWKhZltNkrhbTnJKbX/ms+w=","aunts":["D3sfh6znFeJYVktarjzn2LoCUbqBKlTqLpReoSoHeh4=","mU6FJd54C8957gvYYmaAycmG1MLaj1qpXuPCjO2+Z/U=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":76,"leaf_hash":"COUX5bHlozDgZ9o9h9jOJFU+jTP4BA5jBgT6iJCR5lo=","aunts":["wsDqk8iJsMOYnI/+9b5EFdORvIxLE8FjeHGV+q5cROc=","PedSgccx7hUIxTLBN87Ia9aK1Psq/7qRqlZpM3FQthk=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":77,"leaf_hash":"wsDqk8iJsMOYnI/+9b5EFdORvIxLE8FjeHGV+q5cROc=","aunts":["COUX5bHlozDgZ9o9h9jOJFU+jTP4BA5jBgT6iJCR5lo=","PedSgccx7hUIxTLBN87Ia9aK1Psq/7qRqlZpM3FQthk=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":78,"leaf_hash":"7RZxsStkPi5AAaanNAjpYW34f8OL4l8GmYjFCnns9q8=","aunts":["8UoJMc4zkOU0KMEnIDDDveCg28DCh6V7L8hL+sEegpk=","4CYNTAvJYC5p3V1MlU99xlp9OTtR9cVFqvUpQk3LLOY=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":79,"leaf_hash":"8UoJMc4zkOU0KMEnIDDDveCg28DCh6V7L8hL+sEegpk=","aunts":["7RZxsStkPi5AAaanNAjpYW34f8OL4l8GmYjFCnns9q8=","4CYNTAvJYC5p3V1MlU99xlp9OTtR9cVFqvUpQk3LLOY=","tBfk+6q3CB0XWsN/W4S49DbTzim1g6i4eUsbGbiCrqo=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":80,"leaf_hash":"GXx5ZowNiUJzPW1NTx6O7eO20JivdeJoj5qz9x6BRB4=","aunts":["h8SmsKJGpCoExAZs5eiryH1h8HCjXQYVP1Y2dO17SX0=","2Iv+6tr/xJl2sP5/MwOtIr2J3t2EZAMW1CrvIhyOm6c=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":81,"leaf_hash":"h8SmsKJGpCoExAZs5eiryH1h8HCjXQYVP1Y2dO17SX0=","aunts":["GXx5ZowNiUJzPW1NTx6O7eO20JivdeJoj5qz9x6BRB4=","2Iv+6tr/xJl2sP5/MwOtIr2J3t2EZAMW1CrvIhyOm6c=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":82,"leaf_hash":"MlDvso2902KdiAHE3beuEsLwwtk+1ui5zkxEQ5IKy2w=","aunts":["DUnZo42OM5BjhYMU/ZZ0XdhhFY8QSfu55JvQsctt16g=","eHr4/iJ6pRIvss5IAOLlCxi3GvMgpwGzIxo3k4H7SwI=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":83,"leaf_hash":"DUnZo42OM5BjhYMU/ZZ0XdhhFY8QSfu55JvQsctt16g=","aunts":["MlDvso2902KdiAHE3beuEsLwwtk+1ui5zkxEQ5IKy2w=","eHr4/iJ6pRIvss5IAOLlCxi3GvMgpwGzIxo3k4H7SwI=","g+sV7xmYH0OaMpEXAdAR/AOO9F3WGD5JrU5aZv45mbQ=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":84,"leaf_hash":"yQx/x1F7MLnH4v8whj/A7DTXnLckhLwbPMSJGGOjQeA=","aunts":["pyZYWGZuJ57Hck2aLr+2B6lV/MnjNWSn1oOg3EZ6ZTs=","fCC/SGqvS0jyz0vhWe51MPLxfcJzGTgIuFtJESE0/tY=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":85,"leaf_hash":"pyZYWGZuJ57Hck2aLr+2B6lV/MnjNWSn1oOg3EZ6ZTs=","aunts":["yQx/x1F7MLnH4v8whj/A7DTXnLckhLwbPMSJGGOjQeA=","fCC/SGqvS0jyz0vhWe51MPLxfcJzGTgIuFtJESE0/tY=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":86,"leaf_hash":"OjATVN9Veg7NJFZIRVpu+UY0NuHCQvsBe5OJRKX9OyE=","aunts":["irJQKgIq44t+ynw82tq4EbfmOX+YqEpvcbXqz76HvLc=","Z3gAYFQ+vsI2+cl3q6UqGuerebvPdm5W7dkxrPFsxzU=","DaRYoLFUz9Y9GZRnVFCkewg11+I1/6FIU1lWolCvnSM=","euJHn9uJ5AaHFB0pzvjX3jOJ+cnJdkNXLKJhxDWpICs=","v653N242ui7xbWsc9qjQFAbhUtVzponE1tRNLfy4/Wk=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":74,"end_row":86},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJDrBYiJOdQ7z19aczutMf2mvY8wEzZXCNYBUcgCMBU7b","/////////////////////////////////////////////////////////////////////////////8kk8Gqbi+yE3sPauA988PQ2hY8JMoDrm+RNTTclbXOW"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8Y6NQvFHaLlokrhR+BTwxigHY7ybiG5U9yQGoe+VDRt"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xDfQbvU0Rd67X5rljbhFWi+ur4wDczRKdN4jDCvBInH"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xDl4lGij5uszmj10WHX1OMjN3eJziue9RRqw6ShYkLa"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3rij40yuU5pixs0IGuD187/DABhrLmo9zPDTgoQ46Ml"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////62qPd/xi942tm0UWjCue5Z3wSYRz9DzIWfx1ZKkrYa"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3b2ASMjest9T2+b6hCikigo6eh+kbmOtxydGNCuvsVD"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0HPCtaHAEAEukC8pJpNPbVj5p2MXSJ9su9UURl6i2vx"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0cDZgmLi4aX5jEW2TvhsPQal0/U3HUHLFQodOiUU+7s"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wEkvuZ8jxo9fRztZbkJP31HbPLk10aB5eoxVFOGBINN"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////38MQqKlnNPkea/5QPR7THw6iS5Yrl64ftI3i+CRRs82"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////0d1nkK4ahVsppYlkC4WixsGjD4T8KU8fcm2ymTkQEI"],"is_max_namespace_ignored":true},{"end":67,"nodes":["//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","//////////////////////////////////////7//////////////////////////////////////pNkZFSQk3XAFlq5+qVKnFnJIHi/zkuKy8dZMp9iFzwd","//////////////////////////////////////7//////////////////////////////////////iNa9Eq830VsdytAm6Xke+fTeD22gHTjIOVg5DYjsG9c","//////////////////////////////////////7//////////////////////////////////////i78l7x6hc+r4PDOj/ojJFtChc0fWOiDF42n8pbp/dUO","//////////////////////////////////////7//////////////////////////////////////nv6BM04fuwZggth+O9G6qCRn1vSz9cURJ32UN+p8l7F","/////////////////////////////////////////////////////////////////////////////4AGGpm91fPPTjvlMrv/RYKm9XvKEd3fWCr51Dj+Ed+2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5Mw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MxqhQlHwx6G2tCz73LzgB7Fe3l85116D9Y2ytzCPR6nq","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MywMaCmzQps/Wb1AzmOJuF88xxUEMoVCdjoaAzpe5UU1","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+HJtd/gVnJIk3od5Gb6zmumOYA94IeQhWdbtrwJJeNG","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M6a4XPqBZuaU9FoC84poZACP3lqQ/O4tO2iw1Vw5CVe7","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M7/C6MLgOp9TSaE6DYOVn8tl1HD7a6ngjvZIPrrMXO99","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M8xDHUdCXnXsb400RWPTra5QkBwVwkHUTjvF3bO/mubs","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+GZNKxR6KIbHlVE/Ee9BKykZuttM9ZGYxPwv0Vr8uU8","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+xWAG2oYrZIY9IDxxMdD7ghf89htqItMSFALIYKIxoy","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5Mx8/EPErvByhXhoa3x/Mfhvo8g8CQOj9R8sGh+WW7Odn","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M/UQIcgwcEslRfhKvf9QBgXLGXsOZAM5xwI+WjDiVhVI","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+yxQ4lFRj1sr/rlk6W809he8w7Xs83TN8dt+s8QlOER","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M3pkgToUhFm7wJ8QUSARaWMmrbB2SSqyQns52Ihn613U","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TP//////////////////////////////////////ix6m0IuQCu2e2kdTHI7nrvsBiXG9ULWxyO1O7B9Zvzh"],"proofs":[{"total":512,"index":111,"leaf_hash":"lon6nxclYKAVITg9p7dh52lNBSs5yWra77MJ5/FTimI=","aunts":["bvB/IyNsdtU7RXBexyehw+Q2TGHxJAPYXBR+6lDmhvs=","wq5R+bTkgHH2KMWIQu61uVGfgxiGIcJ+NCyVHWgfHqE=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":112,"leaf_hash":"yvpW8y3HnCCCBdVpSuheND2ZH4L+uUyo3Lxgx6fNpaE=","aunts":["b5ctYExoNQUxelJ9oh12YaGJ6ZkEMlOOO5+opxgFT9c=","KNuXn/kILENls6xAfDMA0oTHft9PHHCrjE5EWCVwxSc=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":113,"leaf_hash":"b5ctYExoNQUxelJ9oh12YaGJ6ZkEMlOOO5+opxgFT9c=","aunts":["yvpW8y3HnCCCBdVpSuheND2ZH4L+uUyo3Lxgx6fNpaE=","KNuXn/kILENls6xAfDMA0oTHft9PHHCrjE5EWCVwxSc=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":114,"leaf_hash":"giNIjEQuh1P/HoliW0XQGI2mI4ZbBQ/oo4ca5AbVJTc=","aunts":["Fk4rzoYwn3CpNzaUqxORvyJJrDiFJDWyg53/QMXVMaw=","Ojaq2OF5oPyD84zZ00WFPnZ6a+leVvw/TUXhPwTVn6c=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":115,"leaf_hash":"Fk4rzoYwn3CpNzaUqxORvyJJrDiFJDWyg53/QMXVMaw=","aunts":["giNIjEQuh1P/HoliW0XQGI2mI4ZbBQ/oo4ca5AbVJTc=","Ojaq2OF5oPyD84zZ00WFPnZ6a+leVvw/TUXhPwTVn6c=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":116,"leaf_hash":"ZlYMuVXx82NWOgZgcZYZd9+emGjUoZKaTjtSfeCHDhM=","aunts":["FQY1hwS+Z1nKWMjJxMhdvyu03tsJqav+eVVoRhNpmLw=","tIY/F481NyK377xzoNTE7oDyCPWnFFJ7SjWp1XaOqKY=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":117,"leaf_hash":"FQY1hwS+Z1nKWMjJxMhdvyu03tsJqav+eVVoRhNpmLw=","aunts":["ZlYMuVXx82NWOgZgcZYZd9+emGjUoZKaTjtSfeCHDhM=","tIY/F481NyK377xzoNTE7oDyCPWnFFJ7SjWp1XaOqKY=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":118,"leaf_hash":"G85RKMf4RKJAKMT73ZIKPHv9LaiWIMZEcaUD6ideqUI=","aunts":["Z6jmLAYRZ9lORrYWHWU1BxWN/JsAJBLHPnUZSawO4og=","gOE39UjHaxmZhwr0KihcTAT0LtcSKNtektIiDw9uzXw=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":119,"leaf_hash":"Z6jmLAYRZ9lORrYWHWU1BxWN/JsAJBLHPnUZSawO4og=","aunts":["G85RKMf4RKJAKMT73ZIKPHv9LaiWIMZEcaUD6ideqUI=","gOE39UjHaxmZhwr0KihcTAT0LtcSKNtektIiDw9uzXw=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":120,"leaf_hash":"HF5hQtM/p0mrSXvBi695lmvwnRRg/8TlKJibHDFuL0w=","aunts":["R2c0WzznXOZgWWNOuOL0KAKfH2MpGuyxiGrU+G0UGSs=","Ec879HlPTACtLH/DenMTS+Lb1zZUUee4QXj/rEBqSqY=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":121,"leaf_hash":"R2c0WzznXOZgWWNOuOL0KAKfH2MpGuyxiGrU+G0UGSs=","aunts":["HF5hQtM/p0mrSXvBi695lmvwnRRg/8TlKJibHDFuL0w=","Ec879HlPTACtLH/DenMTS+Lb1zZUUee4QXj/rEBqSqY=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":122,"leaf_hash":"HywCMZyltrB3tFNIvYB6aYWmzgBfoPI15HeXiVVONRQ=","aunts":["hhDr5idXoCQhB1lKI8wk2d/2PLzFIDTL3J/tNr8lFoI=","EosWa8fqJEgLWZpzNSbiHPFdU6l6zpuJNXJ/7HJrJv8=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":123,"leaf_hash":"hhDr5idXoCQhB1lKI8wk2d/2PLzFIDTL3J/tNr8lFoI=","aunts":["HywCMZyltrB3tFNIvYB6aYWmzgBfoPI15HeXiVVONRQ=","EosWa8fqJEgLWZpzNSbiHPFdU6l6zpuJNXJ/7HJrJv8=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":111,"end_row":123},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":32,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA65DzhFbx5WlTJDrBYiJOdQ7z19aczutMf2mvY8wEzZXCNYBUcgCMBU7b","/////////////////////////////////////////////////////////////////////////////8kk8Gqbi+yE3sPauA988PQ2hY8JMoDrm+RNTTclbXOW"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8Y6NQvFHaLlokrhR+BTwxigHY7ybiG5U9yQGoe+VDRt"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xDfQbvU0Rd67X5rljbhFWi+ur4wDczRKdN4jDCvBInH"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xDl4lGij5uszmj10WHX1OMjN3eJziue9RRqw6ShYkLa"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3rij40yuU5pixs0IGuD187/DABhrLmo9zPDTgoQ46Ml"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////62qPd/xi942tm0UWjCue5Z3wSYRz9DzIWfx1ZKkrYa"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3b2ASMjest9T2+b6hCikigo6eh+kbmOtxydGNCuvsVD"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0HPCtaHAEAEukC8pJpNPbVj5p2MXSJ9su9UURl6i2vx"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0cDZgmLi4aX5jEW2TvhsPQal0/U3HUHLFQodOiUU+7s"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wEkvuZ8jxo9fRztZbkJP31HbPLk10aB5eoxVFOGBINN"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////38MQqKlnNPkea/5QPR7THw6iS5Yrl64ftI3i+CRRs82"],"is_max_namespace_ignored":true},{"end":128,"nodes":["//////////////////////////////////////////////////////////////////////////////0d1nkK4ahVsppYlkC4WixsGjD4T8KU8fcm2ymTkQEI"],"is_max_namespace_ignored":true},{"end":67,"nodes":["//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2","//////////////////////////////////////7//////////////////////////////////////pNkZFSQk3XAFlq5+qVKnFnJIHi/zkuKy8dZMp9iFzwd","//////////////////////////////////////7//////////////////////////////////////iNa9Eq830VsdytAm6Xke+fTeD22gHTjIOVg5DYjsG9c","//////////////////////////////////////7//////////////////////////////////////i78l7x6hc+r4PDOj/ojJFtChc0fWOiDF42n8pbp/dUO","//////////////////////////////////////7//////////////////////////////////////nv6BM04fuwZggth+O9G6qCRn1vSz9cURJ32UN+p8l7F","/////////////////////////////////////////////////////////////////////////////4AGGpm91fPPTjvlMrv/RYKm9XvKEd3fWCr51Dj+Ed+2"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5Mw==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAOuQ84RW8eVpUyQAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MxqhQlHwx6G2tCz73LzgB7Fe3l85116D9Y2ytzCPR6nq","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5MywMaCmzQps/Wb1AzmOJuF88xxUEMoVCdjoaAzpe5UU1","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+HJtd/gVnJIk3od5Gb6zmumOYA94IeQhWdbtrwJJeNG","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M6a4XPqBZuaU9FoC84poZACP3lqQ/O4tO2iw1Vw5CVe7","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M7/C6MLgOp9TSaE6DYOVn8tl1HD7a6ngjvZIPrrMXO99","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M8xDHUdCXnXsb400RWPTra5QkBwVwkHUTjvF3bO/mubs","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+GZNKxR6KIbHlVE/Ee9BKykZuttM9ZGYxPwv0Vr8uU8","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+xWAG2oYrZIY9IDxxMdD7ghf89htqItMSFALIYKIxoy","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5Mx8/EPErvByhXhoa3x/Mfhvo8g8CQOj9R8sGh+WW7Odn","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M/UQIcgwcEslRfhKvf9QBgXLGXsOZAM5xwI+WjDiVhVI","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M+yxQ4lFRj1sr/rlk6W809he8w7Xs83TN8dt+s8QlOER","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TMAAAAAAAAAAAAAAAAAAAAAAAAA82KC+MYa9/H5M3pkgToUhFm7wJ8QUSARaWMmrbB2SSqyQns52Ihn613U","AAAAAAAAAAAAAAAAAAAAAAAAAPNigvjGGvfx+TP//////////////////////////////////////ix6m0IuQCu2e2kdTHI7nrvsBiXG9ULWxyO1O7B9Zvzh"],"proofs":[{"total":512,"index":111,"leaf_hash":"lon6nxclYKAVITg9p7dh52lNBSs5yWra77MJ5/FTimI=","aunts":["bvB/IyNsdtU7RXBexyehw+Q2TGHxJAPYXBR+6lDmhvs=","wq5R+bTkgHH2KMWIQu61uVGfgxiGIcJ+NCyVHWgfHqE=","dVc1ztZvbJEr2WBzLdX33BhB+YA3Niyf1zMVMkc5kRs=","vYtuaswtklrmbpFxD8zt9tp/KjlkPPE+UzMi442XPA4=","zfq2DIfz1FTHqud6O4xC7e13geCwB9jeVKTv5VjsgEo=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":112,"leaf_hash":"yvpW8y3HnCCCBdVpSuheND2ZH4L+uUyo3Lxgx6fNpaE=","aunts":["b5ctYExoNQUxelJ9oh12YaGJ6ZkEMlOOO5+opxgFT9c=","KNuXn/kILENls6xAfDMA0oTHft9PHHCrjE5EWCVwxSc=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":113,"leaf_hash":"b5ctYExoNQUxelJ9oh12YaGJ6ZkEMlOOO5+opxgFT9c=","aunts":["yvpW8y3HnCCCBdVpSuheND2ZH4L+uUyo3Lxgx6fNpaE=","KNuXn/kILENls6xAfDMA0oTHft9PHHCrjE5EWCVwxSc=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":114,"leaf_hash":"giNIjEQuh1P/HoliW0XQGI2mI4ZbBQ/oo4ca5AbVJTc=","aunts":["Fk4rzoYwn3CpNzaUqxORvyJJrDiFJDWyg53/QMXVMaw=","Ojaq2OF5oPyD84zZ00WFPnZ6a+leVvw/TUXhPwTVn6c=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":115,"leaf_hash":"Fk4rzoYwn3CpNzaUqxORvyJJrDiFJDWyg53/QMXVMaw=","aunts":["giNIjEQuh1P/HoliW0XQGI2mI4ZbBQ/oo4ca5AbVJTc=","Ojaq2OF5oPyD84zZ00WFPnZ6a+leVvw/TUXhPwTVn6c=","rdgzck0QWMGuSf8p1r7gSkD3tuobnJrHqaKHAU9yOG0=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":116,"leaf_hash":"ZlYMuVXx82NWOgZgcZYZd9+emGjUoZKaTjtSfeCHDhM=","aunts":["FQY1hwS+Z1nKWMjJxMhdvyu03tsJqav+eVVoRhNpmLw=","tIY/F481NyK377xzoNTE7oDyCPWnFFJ7SjWp1XaOqKY=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":117,"leaf_hash":"FQY1hwS+Z1nKWMjJxMhdvyu03tsJqav+eVVoRhNpmLw=","aunts":["ZlYMuVXx82NWOgZgcZYZd9+emGjUoZKaTjtSfeCHDhM=","tIY/F481NyK377xzoNTE7oDyCPWnFFJ7SjWp1XaOqKY=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":118,"leaf_hash":"G85RKMf4RKJAKMT73ZIKPHv9LaiWIMZEcaUD6ideqUI=","aunts":["Z6jmLAYRZ9lORrYWHWU1BxWN/JsAJBLHPnUZSawO4og=","gOE39UjHaxmZhwr0KihcTAT0LtcSKNtektIiDw9uzXw=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":119,"leaf_hash":"Z6jmLAYRZ9lORrYWHWU1BxWN/JsAJBLHPnUZSawO4og=","aunts":["G85RKMf4RKJAKMT73ZIKPHv9LaiWIMZEcaUD6ideqUI=","gOE39UjHaxmZhwr0KihcTAT0LtcSKNtektIiDw9uzXw=","MZv5VmtIpFWTan1aL2z+5M7wTgqP09/GXFTQpN87tCo=","EYiHvI18oViYvXWF014rIb2YM5AlIqsemO8gqmBZDi8=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":120,"leaf_hash":"HF5hQtM/p0mrSXvBi695lmvwnRRg/8TlKJibHDFuL0w=","aunts":["R2c0WzznXOZgWWNOuOL0KAKfH2MpGuyxiGrU+G0UGSs=","Ec879HlPTACtLH/DenMTS+Lb1zZUUee4QXj/rEBqSqY=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":121,"leaf_hash":"R2c0WzznXOZgWWNOuOL0KAKfH2MpGuyxiGrU+G0UGSs=","aunts":["HF5hQtM/p0mrSXvBi695lmvwnRRg/8TlKJibHDFuL0w=","Ec879HlPTACtLH/DenMTS+Lb1zZUUee4QXj/rEBqSqY=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":122,"leaf_hash":"HywCMZyltrB3tFNIvYB6aYWmzgBfoPI15HeXiVVONRQ=","aunts":["hhDr5idXoCQhB1lKI8wk2d/2PLzFIDTL3J/tNr8lFoI=","EosWa8fqJEgLWZpzNSbiHPFdU6l6zpuJNXJ/7HJrJv8=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":123,"leaf_hash":"hhDr5idXoCQhB1lKI8wk2d/2PLzFIDTL3J/tNr8lFoI=","aunts":["HywCMZyltrB3tFNIvYB6aYWmzgBfoPI15HeXiVVONRQ=","EosWa8fqJEgLWZpzNSbiHPFdU6l6zpuJNXJ/7HJrJv8=","dIP8OBYFKfwJ8lH+/Uz0vdG5dayJ6ynoszk5rZLZ9sI=","UylsZBbcTCZQKxoJLfTIAdkro9BK+hsE38dE8Rn6nUM=","mxle8IvRlekXyK4CSn3R2pYtsSXeqVR5LVTAVCMVAE8=","KqmSZq03qAYkdb8qpfA3tX0YmS3QqSe7XeIUfEI1Oe8=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":111,"end_row":123},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":96,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8ZDvN8f1AhJlJ/zwW8dtpBgVjcgs3UznMV9EDJCQgsp2","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8XwZiTI5w5FRZPfKQwaXO4YkHe5a0c4zJ+m/eOG3Mipy","/////////////////////////////////////////////////////////////////////////////7KhpG1So7lDEnZoqOkA/wq+/SMAvIKKx1MqoSgx37AA"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1zaa9YhQMMSmRn7DcVXf1w1xlati+0G2El/xaJ0JD4v"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////z5v49NEHyHuHHVo7HIKrG5HG3KOrwdrolqCtS7+GH3s"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7AXZEjimQ4OsUv6DgQYClVIXJytu+hR9Z+fIQazhu0B"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////4S4pzis4RI2xlmYlhzPO9Otzpk+dtqNBcpybP7a2ulv"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wNFdOSEj1OrrFb0DQvAFsmjOw462zPjVqNpvQzbPOy4"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////x86K2y1WrEf6AzQMyp69NNgiOny6Yp7jAAsbL9nmU5K"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////y52/DDJPUQt7I+wvmLIhHN37eW+jLSnq6JOjmdQNSc7"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+FMVkNOwvgUWopbODuRv65Hkwi8LHShOpn2l0Yagmmr"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////54lJ1j18a4aTh8xro1VrHUFauAysQ3COVMF++g8zvTq"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wz5B/yu+kvxc+kOm4ZwakBTBxUFh4XxroVVSFNzoaYM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7Guc+RcRVCQr4XZcAtWVEs27I11+mAvHcw5bGMlx+FF"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+dyBRe91H8lxDpr5tFm9G70c+HedmzLAfV8k/ItpnZx"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMv07QS57LTB6iEbPZTxnRWHdi5IYPJlFBuJ4LCmhX0HO","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMqwIYJTvFiXwwQAfq5RQIixH8T1N14sMiBQwq563oqMo","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMpP/7T+r+iSCti+Z9BLmkjsSomEXpEx6se+Ll7HjSjPa","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMmsWxm1FpX6ldktW2BY7vNXbuzMW0+CbwSU1V5iNw1UA","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0K1lIHmfVxoWvPeefgtIw58Vice4Z7yS1jRK5EqeQtN","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc8CFq3t0o4i8v3iA7122U6By2SQlblyWA+i7M7R7K/s+","/////////////////////////////////////////////////////////////////////////////yKtOfSCDhJXTtE/jqR6axp5wfNWyCcXeIJwzfj55qhB"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMsfjZqMT/6wH0zNNT5dm+1n9WcvwrsvkQBbQci7AEdrN","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMtuaLx358R3TEyF7OIp/tCcYGPvdv1lFNNOjv5q6mprC","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMrfdppvAsiIlZFYA4OmTKmZC2OJK8LYQPMezuNCTEeTA","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMojbKn9qQ7RPwOE0Umjv//eKi8q0P6DHpR/uQpcB8JTJ","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMrte8vPO82cUIpuwc4eNcppOn4Gz/WajJ76HmADIRlwP","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMugZNlshEceJsAadoz/3aKwV8Y88UXb9QTB1WixeTzR5","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMtEx/9O5gdup1EqxMPwjPzsE225+jwJTZYEN/bcKbrwY","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMhsmHhqDkcJJx34Q5g8g9JBJO4m46NwkFh6Bw39Bgzu8","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMv1lmOw26WEb9vW2mUzcIggq05/FtwoPjaGVHocKipgc","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMtM1UFaML9GbKUEkA4Slt77WW797YPXtomgh1kufQGwR","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMgJKpeVXq9+CoT1W7SGqY/SGQbT1qzGzIYKDQGbVGHjg","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMjLDj05ossie8I3awunLT8A6WAf2Mon/yiIzY2FWNOCK","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMr7bap7/0k/VET9gQFo5TIaN7/6rjsKO4XRzp3/H+0fY","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc7C7SEmLT0ttB9wvClOreUyfrC4VXm3l3wFZ1x6/7R8C"],"proofs":[{"total":512,"index":61,"leaf_hash":"sP3w+FHp+FtoP2u6DDZFeCUw3f9QFA3v9h26cdn4m3Q=","aunts":["1E4AQpfmlNTv8FWqkRwjitHnsOQoGiWyJvZup54QM98=","yQQmrkLqQUC5Qb3Nfs0c1GCgLhcKs/9FNWYtbNZlVDA=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":62,"leaf_hash":"pRtmuyYB6tgBB5eyAII2JvfxbD1WV1yueGVoDT06tLA=","aunts":["aK2UrsI00VOQSFayHm6Wdljul8xVHKMvY81nldrbOzk=","pMoGNfmvZrExvS6KYIxA96Zm9EveuzR1mOBxZTH+oxs=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":63,"leaf_hash":"aK2UrsI00VOQSFayHm6Wdljul8xVHKMvY81nldrbOzk=","aunts":["pRtmuyYB6tgBB5eyAII2JvfxbD1WV1yueGVoDT06tLA=","pMoGNfmvZrExvS6KYIxA96Zm9EveuzR1mOBxZTH+oxs=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":64,"leaf_hash":"oQM4Ctm6GBN/X3Qp+epB063kvNqEQoD6p1df0HFDHSA=","aunts":["zd/l+S7BvVjnUQbF1YsFurqR8DGdG2/Dv0VC+E4QFf0=","Eth3hm2pqD6og8P21ja1Ufnwi80KXE+HBJ22KmIEJFc=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":65,"leaf_hash":"zd/l+S7BvVjnUQbF1YsFurqR8DGdG2/Dv0VC+E4QFf0=","aunts":["oQM4Ctm6GBN/X3Qp+epB063kvNqEQoD6p1df0HFDHSA=","Eth3hm2pqD6og8P21ja1Ufnwi80KXE+HBJ22KmIEJFc=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":66,"leaf_hash":"URTf0q3m2Id+t9gZwfOCyby7pwT7x2151Kq9fhtDaYM=","aunts":["3gxWoO/fgevBQ2t1LsrxVzGkArbKMu/t6vPo2hF3oqE=","IKszOkSWWDniU5vJHJ6EZazufT5nOxYpV+eudkRMpyU=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":67,"leaf_hash":"3gxWoO/fgevBQ2t1LsrxVzGkArbKMu/t6vPo2hF3oqE=","aunts":["URTf0q3m2Id+t9gZwfOCyby7pwT7x2151Kq9fhtDaYM=","IKszOkSWWDniU5vJHJ6EZazufT5nOxYpV+eudkRMpyU=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":68,"leaf_hash":"WWMnRcyN7iBLsDkgIBDu1lcuNOk6eD6Fh6FIs4T/5Mo=","aunts":["9xf+squ69XFn6Ul26kJwPTxFuZvU4eSaIsWlSNq8vlM=","mP5S/j8oUFW+bwtyg/KNByNL0X1yFodB6MWnd9wexwA=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":69,"leaf_hash":"9xf+squ69XFn6Ul26kJwPTxFuZvU4eSaIsWlSNq8vlM=","aunts":["WWMnRcyN7iBLsDkgIBDu1lcuNOk6eD6Fh6FIs4T/5Mo=","mP5S/j8oUFW+bwtyg/KNByNL0X1yFodB6MWnd9wexwA=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":70,"leaf_hash":"lSlqBbaYdW4/ZOTdkNBYzkQJtUp4DMd18BI2Xj5Mdq8=","aunts":["YwCz7x2e7rE8r0BHfqqwVYmxaxby+7l0lRhrLhhFjao=","ktXfDRGi2A3eByV12GCR4Dui6WPQCWXXs2xEdMax9vU=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":71,"leaf_hash":"YwCz7x2e7rE8r0BHfqqwVYmxaxby+7l0lRhrLhhFjao=","aunts":["lSlqBbaYdW4/ZOTdkNBYzkQJtUp4DMd18BI2Xj5Mdq8=","ktXfDRGi2A3eByV12GCR4Dui6WPQCWXXs2xEdMax9vU=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":72,"leaf_hash":"SlqC8CZXThiKkvvzm5WfwelHiNQrdRTMPuCHmcOFe9U=","aunts":["4mpfmyJW0Wp1NvgYU8d0i5EI/5GvPyrUECepZsX/XzU=","d3g9A0ZGazZkBQuiDvfKK2XuPoP2FmA3IySSEcUi4y0=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":73,"leaf_hash":"4mpfmyJW0Wp1NvgYU8d0i5EI/5GvPyrUECepZsX/XzU=","aunts":["SlqC8CZXThiKkvvzm5WfwelHiNQrdRTMPuCHmcOFe9U=","d3g9A0ZGazZkBQuiDvfKK2XuPoP2FmA3IySSEcUi4y0=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":74,"leaf_hash":"D3sfh6znFeJYVktarjzn2LoCUbqBKlTqLpReoSoHeh4=","aunts":["wwwOikEBsAd7rtba8sp8TWKhZltNkrhbTnJKbX/ms+w=","mU6FJd54C8957gvYYmaAycmG1MLaj1qpXuPCjO2+Z/U=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":61,"end_row":74},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":96,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8ZDvN8f1AhJlJ/zwW8dtpBgVjcgs3UznMV9EDJCQgsp2","AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAV+/LSZkfLifa8XwZiTI5w5FRZPfKQwaXO4YkHe5a0c4zJ+m/eOG3Mipy","/////////////////////////////////////////////////////////////////////////////7KhpG1So7lDEnZoqOkA/wq+/SMAvIKKx1MqoSgx37AA"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////1zaa9YhQMMSmRn7DcVXf1w1xlati+0G2El/xaJ0JD4v"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////z5v49NEHyHuHHVo7HIKrG5HG3KOrwdrolqCtS7+GH3s"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7AXZEjimQ4OsUv6DgQYClVIXJytu+hR9Z+fIQazhu0B"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////4S4pzis4RI2xlmYlhzPO9Otzpk+dtqNBcpybP7a2ulv"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wNFdOSEj1OrrFb0DQvAFsmjOw462zPjVqNpvQzbPOy4"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////x86K2y1WrEf6AzQMyp69NNgiOny6Yp7jAAsbL9nmU5K"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////y52/DDJPUQt7I+wvmLIhHN37eW+jLSnq6JOjmdQNSc7"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+FMVkNOwvgUWopbODuRv65Hkwi8LHShOpn2l0Yagmmr"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////54lJ1j18a4aTh8xro1VrHUFauAysQ3COVMF++g8zvTq"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////wz5B/yu+kvxc+kOm4ZwakBTBxUFh4XxroVVSFNzoaYM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////7Guc+RcRVCQr4XZcAtWVEs27I11+mAvHcw5bGMlx+FF"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+dyBRe91H8lxDpr5tFm9G70c+HedmzLAfV8k/ItpnZx"],"is_max_namespace_ignored":true},{"end":5,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMv07QS57LTB6iEbPZTxnRWHdi5IYPJlFBuJ4LCmhX0HO","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMqwIYJTvFiXwwQAfq5RQIixH8T1N14sMiBQwq563oqMo","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMpP/7T+r+iSCti+Z9BLmkjsSomEXpEx6se+Ll7HjSjPa","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMmsWxm1FpX6ldktW2BY7vNXbuzMW0+CbwSU1V5iNw1UA","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc0K1lIHmfVxoWvPeefgtIw58Vice4Z7yS1jRK5EqeQtN","AAAAAAAAAAAAAAAAAAAAAAAAAKBsrPDUKcR2FHMAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc8CFq3t0o4i8v3iA7122U6By2SQlblyWA+i7M7R7K/s+","/////////////////////////////////////////////////////////////////////////////yKtOfSCDhJXTtE/jqR6axp5wfNWyCcXeIJwzfj55qhB"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAAFfvy0mZHy4n2vEAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMsfjZqMT/6wH0zNNT5dm+1n9WcvwrsvkQBbQci7AEdrN","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMtuaLx358R3TEyF7OIp/tCcYGPvdv1lFNNOjv5q6mprC","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMrfdppvAsiIlZFYA4OmTKmZC2OJK8LYQPMezuNCTEeTA","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMojbKn9qQ7RPwOE0Umjv//eKi8q0P6DHpR/uQpcB8JTJ","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMrte8vPO82cUIpuwc4eNcppOn4Gz/WajJ76HmADIRlwP","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMugZNlshEceJsAadoz/3aKwV8Y88UXb9QTB1WixeTzR5","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMtEx/9O5gdup1EqxMPwjPzsE225+jwJTZYEN/bcKbrwY","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMhsmHhqDkcJJx34Q5g8g9JBJO4m46NwkFh6Bw39Bgzu8","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMv1lmOw26WEb9vW2mUzcIggq05/FtwoPjaGVHocKipgc","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMtM1UFaML9GbKUEkA4Slt77WW797YPXtomgh1kufQGwR","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMgJKpeVXq9+CoT1W7SGqY/SGQbT1qzGzIYKDQGbVGHjg","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMjLDj05ossie8I3awunLT8A6WAf2Mon/yiIzY2FWNOCK","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAiWuiTBBlhTWyMr7bap7/0k/VET9gQFo5TIaN7/6rjsKO4XRzp3/H+0fY","AAAAAAAAAAAAAAAAAAAAAAAAAIlrokwQZYU1sjIAAAAAAAAAAAAAAAAAAAAAAAAAoGys8NQpxHYUc7C7SEmLT0ttB9wvClOreUyfrC4VXm3l3wFZ1x6/7R8C"],"proofs":[{"total":512,"index":61,"leaf_hash":"sP3w+FHp+FtoP2u6DDZFeCUw3f9QFA3v9h26cdn4m3Q=","aunts":["1E4AQpfmlNTv8FWqkRwjitHnsOQoGiWyJvZup54QM98=","yQQmrkLqQUC5Qb3Nfs0c1GCgLhcKs/9FNWYtbNZlVDA=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":62,"leaf_hash":"pRtmuyYB6tgBB5eyAII2JvfxbD1WV1yueGVoDT06tLA=","aunts":["aK2UrsI00VOQSFayHm6Wdljul8xVHKMvY81nldrbOzk=","pMoGNfmvZrExvS6KYIxA96Zm9EveuzR1mOBxZTH+oxs=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":63,"leaf_hash":"aK2UrsI00VOQSFayHm6Wdljul8xVHKMvY81nldrbOzk=","aunts":["pRtmuyYB6tgBB5eyAII2JvfxbD1WV1yueGVoDT06tLA=","pMoGNfmvZrExvS6KYIxA96Zm9EveuzR1mOBxZTH+oxs=","xj6L5qwp8yoJMc7Mcnw6pKIxBX5cXn9/EO5C0YYl29Q=","OBTYcWlIgXWml0707mogRAukt2pnQ2zAV8P6KIigoIc=","AJcakdHbDFk+C8JpTwxEtPexYON+e2VZ5xetn0xPeGA=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":64,"leaf_hash":"oQM4Ctm6GBN/X3Qp+epB063kvNqEQoD6p1df0HFDHSA=","aunts":["zd/l+S7BvVjnUQbF1YsFurqR8DGdG2/Dv0VC+E4QFf0=","Eth3hm2pqD6og8P21ja1Ufnwi80KXE+HBJ22KmIEJFc=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":65,"leaf_hash":"zd/l+S7BvVjnUQbF1YsFurqR8DGdG2/Dv0VC+E4QFf0=","aunts":["oQM4Ctm6GBN/X3Qp+epB063kvNqEQoD6p1df0HFDHSA=","Eth3hm2pqD6og8P21ja1Ufnwi80KXE+HBJ22KmIEJFc=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":66,"leaf_hash":"URTf0q3m2Id+t9gZwfOCyby7pwT7x2151Kq9fhtDaYM=","aunts":["3gxWoO/fgevBQ2t1LsrxVzGkArbKMu/t6vPo2hF3oqE=","IKszOkSWWDniU5vJHJ6EZazufT5nOxYpV+eudkRMpyU=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":67,"leaf_hash":"3gxWoO/fgevBQ2t1LsrxVzGkArbKMu/t6vPo2hF3oqE=","aunts":["URTf0q3m2Id+t9gZwfOCyby7pwT7x2151Kq9fhtDaYM=","IKszOkSWWDniU5vJHJ6EZazufT5nOxYpV+eudkRMpyU=","U382MpLnRaXzYDij1Rmxx68WMQC+dDk2ptgROq99JK0=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":68,"leaf_hash":"WWMnRcyN7iBLsDkgIBDu1lcuNOk6eD6Fh6FIs4T/5Mo=","aunts":["9xf+squ69XFn6Ul26kJwPTxFuZvU4eSaIsWlSNq8vlM=","mP5S/j8oUFW+bwtyg/KNByNL0X1yFodB6MWnd9wexwA=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":69,"leaf_hash":"9xf+squ69XFn6Ul26kJwPTxFuZvU4eSaIsWlSNq8vlM=","aunts":["WWMnRcyN7iBLsDkgIBDu1lcuNOk6eD6Fh6FIs4T/5Mo=","mP5S/j8oUFW+bwtyg/KNByNL0X1yFodB6MWnd9wexwA=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":70,"leaf_hash":"lSlqBbaYdW4/ZOTdkNBYzkQJtUp4DMd18BI2Xj5Mdq8=","aunts":["YwCz7x2e7rE8r0BHfqqwVYmxaxby+7l0lRhrLhhFjao=","ktXfDRGi2A3eByV12GCR4Dui6WPQCWXXs2xEdMax9vU=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":71,"leaf_hash":"YwCz7x2e7rE8r0BHfqqwVYmxaxby+7l0lRhrLhhFjao=","aunts":["lSlqBbaYdW4/ZOTdkNBYzkQJtUp4DMd18BI2Xj5Mdq8=","ktXfDRGi2A3eByV12GCR4Dui6WPQCWXXs2xEdMax9vU=","CdpLSpV3Tk2Tv0uQ3cSq6SaMyvGKtdp/incDNK//2Aw=","fuj/fbHELS0ENRbDDgYOwClDxVroWoQcxIZhywYkw5s=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":72,"leaf_hash":"SlqC8CZXThiKkvvzm5WfwelHiNQrdRTMPuCHmcOFe9U=","aunts":["4mpfmyJW0Wp1NvgYU8d0i5EI/5GvPyrUECepZsX/XzU=","d3g9A0ZGazZkBQuiDvfKK2XuPoP2FmA3IySSEcUi4y0=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":73,"leaf_hash":"4mpfmyJW0Wp1NvgYU8d0i5EI/5GvPyrUECepZsX/XzU=","aunts":["SlqC8CZXThiKkvvzm5WfwelHiNQrdRTMPuCHmcOFe9U=","d3g9A0ZGazZkBQuiDvfKK2XuPoP2FmA3IySSEcUi4y0=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":74,"leaf_hash":"D3sfh6znFeJYVktarjzn2LoCUbqBKlTqLpReoSoHeh4=","aunts":["wwwOikEBsAd7rtba8sp8TWKhZltNkrhbTnJKbX/ms+w=","mU6FJd54C8957gvYYmaAycmG1MLaj1qpXuPCjO2+Z/U=","0FO7rvQHcRN3wSBRQI0QqbB8IR9UDTaa9VutMwau94E=","Errf+iRQVeOlVzZjjiUnCAbodPbOfyi3HiCoBdc5s3Y=","WYy7vT4HdqD5RKS1I0/bOdjjU26seJS834pi2Xc1GJ0=","lvNijcQVIldZp1GJl0QBIttA+v19wL9taZnocZBMivs=","TW1FuncH5VQEFTrM2EVyIDa7QtQnFX/sq0ojKTzq3oU=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":61,"end_row":74},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":96,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314tejl6dYzg8D9PuWdqiY4Y7ihSQ047FfB8DMrh+yIEfX","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314rNy2AMzgVHoYL0Y1Lp6SN82I3VczbuhGZ+cL64ew97B","/////////////////////////////////////////////////////////////////////////////7XDREmTiwZwpguDCbCDaWadKNGBLSrXGDdV23EJsKhS"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////ywII2phsYW6f1JoHA/0HOUUH0Kxfa0HH2hzW0Op8C9c"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8jNfIZ65uZG1TWqr521SxMsC6GDfarodCkq7pRBN0/D"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////07kQLOqDZdaVZEqPAIRCdP0omzrkxLcv9cL9eFHHYnO"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6/mVzMxG7H1o+qts3JSq1c2p5N8FE+IGdKHex/urqpn"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3YrCtA1uj2dO1ZJTs6tNNrikqUo5rvnMFEoua9DvTE1"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////60IxiTuAF3y5tMbh7+1p4cJ6D2Qb6EYwYvAgwYALgxZ"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zfyXls6+DIOjLQ1t1oez1L4LkcmeOOWJT9mFAGVlqoo"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3F0mEe+UwsP5CRj3UwIeGm9x6ky5UnFsWsawuwsl3IY"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0RIwFSpmSQrifpZxJ4HOITtlCSO7oiDdj86gjRPUAyv"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+lfyuvtzuHwZ9QtKMv4IxblFDMExDug3dPDBetxQIcM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xV0NrFYp2tZaTjDVqu84B54qC9MrnqniMnHhmF5Ac9f"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yfgW0FA/HywzRWoe+Z6kXwJrKThjW3yKiCVAjsT6ZuR"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizupd+AyS5u9jCO5lMZmKW0DO9QMFQa7qorlqOVbuPfvK","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizlauXczyvvv3Xj2+mubuWMUk0duGv+DKLvB3PnVk/8Kd","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizjwVGGkyux1PF788Qo6oeM2IzbBuXY7sC0K4bAH2w95i","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6rP6BGmWqhRjsgKEohT4J1aevH2NcAeYIGnkmlNewr7I","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6k8Uf1VzdLJbuvKjt9FLodsBkJWLEAX5qxyOw+MXCjwA","/////////////////////////////////////////////////////////////////////////////wpN0WsQhEbamwxNSF3v+5QmVOOeqsBX5GH90ON44Ggg"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrEFVEynmuRAbggTEYYqyVtjoA+pubEy+NMwNDeP7LZt","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizvuupmzolAU+n7jL2iKh0b1umaok7exMrVr4TwE7f7Ty","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizsQ5JcstXttqibj58EkCTM9SYkqH6bULTlrzKG7nmEfy","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizhSiqAj3sepirhCw/Fchm74v0ePy8HHUgcQoW0J4AfPy","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizsG+F+S6SRymdXIF8GIzTUmcvWy8w75ccG4fiOgJOBE+","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrpwq24huHOwb34Hh/vGz0YHzn2m4Lx+pi7FfTSgxjvu","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOiznXfMOYSbcDr+V/wAU5FDHvK48WuC5wbmRX7DPe6Ia2g","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizg5lBKQy4HPrPJA6hpIb1YilZc9iSc9ArBnKwDUeokGv","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizlXP1svbaYO5rLjHkODpf/oDifsyH5UFY5IObbirYhLt","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizltvS/78IM9C3SzExu4H4dPVvzMe+Bmu8xivNWPGRh+S","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizhTfKUXqyFXgtWe+HxX0we1h50hBS4QOrhfR8qC+kV1T","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizgf+XwzMm6eg4j4B3E71G5lGUAxlddCDevc9+qt0tKp0","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrHgGpi1fyfEHk2jGNDDSOEguVuwSjPZk6txefaV5K8h","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6lw7mcgxgJmgYlYsdf2f0jVDWoEgP9/5tuFHDjDISY0v"],"proofs":[{"total":512,"index":24,"leaf_hash":"4DOlpLonw/S8WfZGA8Ta9r3L1/6a2jJVi1v3os6HsdE=","aunts":["jC0w1LLr8sVZGsQbfAFGJxIJ7MrVblPMgREbKKKhr0U=","lUIHwdjN0WX5pTX3QjKoAJywZOtft8jCaYItP36T0dk=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":25,"leaf_hash":"jC0w1LLr8sVZGsQbfAFGJxIJ7MrVblPMgREbKKKhr0U=","aunts":["4DOlpLonw/S8WfZGA8Ta9r3L1/6a2jJVi1v3os6HsdE=","lUIHwdjN0WX5pTX3QjKoAJywZOtft8jCaYItP36T0dk=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":26,"leaf_hash":"6YLM+QfJBWKoaHE9vKs2KNkovvrMHstm8qLPVVkVdLY=","aunts":["vrT8QOmnjz5D23Q5VFiTGPqcUuciGAZIC/t0De5jHKA=","dREcY8qqq4drNRq2EoakhGxiKXEMOKLNTyx2eprygAI=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":27,"leaf_hash":"vrT8QOmnjz5D23Q5VFiTGPqcUuciGAZIC/t0De5jHKA=","aunts":["6YLM+QfJBWKoaHE9vKs2KNkovvrMHstm8qLPVVkVdLY=","dREcY8qqq4drNRq2EoakhGxiKXEMOKLNTyx2eprygAI=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":28,"leaf_hash":"fAtgAsvgcisKSRREAPyp5Wx3DIkTVI3KiZW46IyrS9E=","aunts":["XX9ZmbS4qFV3kxWq/nepAUV9HWgZZTRkgYCF9niPgHQ=","3RkN60amzXVt6bT5LYEiFSlgk9QxFzjSjo1sKwa6EEc=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":29,"leaf_hash":"XX9ZmbS4qFV3kxWq/nepAUV9HWgZZTRkgYCF9niPgHQ=","aunts":["fAtgAsvgcisKSRREAPyp5Wx3DIkTVI3KiZW46IyrS9E=","3RkN60amzXVt6bT5LYEiFSlgk9QxFzjSjo1sKwa6EEc=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":30,"leaf_hash":"V+MgqfXP/Xp6dbPEIyKyCSQRWN+o33ZyokIibkB+qDE=","aunts":["fVymoDyU+rbk0ySwSkrvPE8Lsk2U5pfbZu8D4ZvHnF0=","+p2B7bEGuFuR9PQjmq2hVeu7FMS+JAxf+PjKtAuFi3w=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":31,"leaf_hash":"fVymoDyU+rbk0ySwSkrvPE8Lsk2U5pfbZu8D4ZvHnF0=","aunts":["V+MgqfXP/Xp6dbPEIyKyCSQRWN+o33ZyokIibkB+qDE=","+p2B7bEGuFuR9PQjmq2hVeu7FMS+JAxf+PjKtAuFi3w=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":32,"leaf_hash":"1AarVZQ7Y47h1adoEl9f3O4RqBb1ktkXd2RW/qUR9ho=","aunts":["HdQ93Ga5tmcPqkJxfM3GDXgX2hHSulxHJzGILg6xz1o=","6SS/ezUPPP9Vhmp8kHQv9NLWxAN9eppULCShGetHxtg=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":33,"leaf_hash":"HdQ93Ga5tmcPqkJxfM3GDXgX2hHSulxHJzGILg6xz1o=","aunts":["1AarVZQ7Y47h1adoEl9f3O4RqBb1ktkXd2RW/qUR9ho=","6SS/ezUPPP9Vhmp8kHQv9NLWxAN9eppULCShGetHxtg=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":34,"leaf_hash":"KuOtbviKOmPFTwmWuGJjJUsTmONpK5siQgC+CbpzknA=","aunts":["ByblfMUKeilTtBWaTLVkmBIHNMhkecceGiPSgLa8KY0=","Nv8kesrFhTw7nwDYeOgfvrORHhiv6I+aLl7oZmFEaBw=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":35,"leaf_hash":"ByblfMUKeilTtBWaTLVkmBIHNMhkecceGiPSgLa8KY0=","aunts":["KuOtbviKOmPFTwmWuGJjJUsTmONpK5siQgC+CbpzknA=","Nv8kesrFhTw7nwDYeOgfvrORHhiv6I+aLl7oZmFEaBw=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":36,"leaf_hash":"uAg7ghc128txB+MPmtveLuOYIF1IWiWOPsFBHuJ2PE4=","aunts":["H6Bsm1vo5LPYh6Cw3OKquRbXrAbMUEd7J4MK6Q/uBKk=","3I20LGLactE5JAKpAcEnrJHltyDE2zbGv6bdXu2KSe8=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":37,"leaf_hash":"H6Bsm1vo5LPYh6Cw3OKquRbXrAbMUEd7J4MK6Q/uBKk=","aunts":["uAg7ghc128txB+MPmtveLuOYIF1IWiWOPsFBHuJ2PE4=","3I20LGLactE5JAKpAcEnrJHltyDE2zbGv6bdXu2KSe8=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":24,"end_row":37},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64},{"commitment_proof":{"subtree_roots":["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"],"subtree_root_proofs":[{"start":96,"end":128,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314tejl6dYzg8D9PuWdqiY4Y7ihSQ047FfB8DMrh+yIEfX","AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAPGp1KOI9td314rNy2AMzgVHoYL0Y1Lp6SN82I3VczbuhGZ+cL64ew97B","/////////////////////////////////////////////////////////////////////////////7XDREmTiwZwpguDCbCDaWadKNGBLSrXGDdV23EJsKhS"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////ywII2phsYW6f1JoHA/0HOUUH0Kxfa0HH2hzW0Op8C9c"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////8jNfIZ65uZG1TWqr521SxMsC6GDfarodCkq7pRBN0/D"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////07kQLOqDZdaVZEqPAIRCdP0omzrkxLcv9cL9eFHHYnO"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////6/mVzMxG7H1o+qts3JSq1c2p5N8FE+IGdKHex/urqpn"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3YrCtA1uj2dO1ZJTs6tNNrikqUo5rvnMFEoua9DvTE1"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////60IxiTuAF3y5tMbh7+1p4cJ6D2Qb6EYwYvAgwYALgxZ"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////zfyXls6+DIOjLQ1t1oez1L4LkcmeOOWJT9mFAGVlqoo"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////3F0mEe+UwsP5CRj3UwIeGm9x6ky5UnFsWsawuwsl3IY"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////0RIwFSpmSQrifpZxJ4HOITtlCSO7oiDdj86gjRPUAyv"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////+lfyuvtzuHwZ9QtKMv4IxblFDMExDug3dPDBetxQIcM"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////xV0NrFYp2tZaTjDVqu84B54qC9MrnqniMnHhmF5Ac9f"],"is_max_namespace_ignored":true},{"end":128,"nodes":["/////////////////////////////////////////////////////////////////////////////yfgW0FA/HywzRWoe+Z6kXwJrKThjW3yKiCVAjsT6ZuR"],"is_max_namespace_ignored":true},{"end":7,"nodes":["AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizupd+AyS5u9jCO5lMZmKW0DO9QMFQa7qorlqOVbuPfvK","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizlauXczyvvv3Xj2+mubuWMUk0duGv+DKLvB3PnVk/8Kd","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizjwVGGkyux1PF788Qo6oeM2IzbBuXY7sC0K4bAH2w95i","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6rP6BGmWqhRjsgKEohT4J1aevH2NcAeYIGnkmlNewr7I","AAAAAAAAAAAAAAAAAAAAAAAAAETeATjsiZYuUeoAAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6k8Uf1VzdLJbuvKjt9FLodsBkJWLEAX5qxyOw+MXCjwA","/////////////////////////////////////////////////////////////////////////////wpN0WsQhEbamwxNSF3v+5QmVOOeqsBX5GH90ON44Ggg"],"is_max_namespace_ignored":true}],"namespace_id":"AAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizg==","row_proof":{"row_roots":["AAAAAAAAAAAAAAAAAAAAAAAAADxqdSjiPbXd9eIAAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrEFVEynmuRAbggTEYYqyVtjoA+pubEy+NMwNDeP7LZt","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizvuupmzolAU+n7jL2iKh0b1umaok7exMrVr4TwE7f7Ty","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizsQ5JcstXttqibj58EkCTM9SYkqH6bULTlrzKG7nmEfy","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizhSiqAj3sepirhCw/Fchm74v0ePy8HHUgcQoW0J4AfPy","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizsG+F+S6SRymdXIF8GIzTUmcvWy8w75ccG4fiOgJOBE+","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrpwq24huHOwb34Hh/vGz0YHzn2m4Lx+pi7FfTSgxjvu","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOiznXfMOYSbcDr+V/wAU5FDHvK48WuC5wbmRX7DPe6Ia2g","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizg5lBKQy4HPrPJA6hpIb1YilZc9iSc9ArBnKwDUeokGv","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizlXP1svbaYO5rLjHkODpf/oDifsyH5UFY5IObbirYhLt","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizltvS/78IM9C3SzExu4H4dPVvzMe+Bmu8xivNWPGRh+S","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizhTfKUXqyFXgtWe+HxX0we1h50hBS4QOrhfR8qC+kV1T","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizgf+XwzMm6eg4j4B3E71G5lGUAxlddCDevc9+qt0tKp0","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAAQIvr2R3KenOizrHgGpi1fyfEHk2jGNDDSOEguVuwSjPZk6txefaV5K8h","AAAAAAAAAAAAAAAAAAAAAAAAAECL69kdynpzos4AAAAAAAAAAAAAAAAAAAAAAAAARN4BOOyJli5R6lw7mcgxgJmgYlYsdf2f0jVDWoEgP9/5tuFHDjDISY0v"],"proofs":[{"total":512,"index":24,"leaf_hash":"4DOlpLonw/S8WfZGA8Ta9r3L1/6a2jJVi1v3os6HsdE=","aunts":["jC0w1LLr8sVZGsQbfAFGJxIJ7MrVblPMgREbKKKhr0U=","lUIHwdjN0WX5pTX3QjKoAJywZOtft8jCaYItP36T0dk=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":25,"leaf_hash":"jC0w1LLr8sVZGsQbfAFGJxIJ7MrVblPMgREbKKKhr0U=","aunts":["4DOlpLonw/S8WfZGA8Ta9r3L1/6a2jJVi1v3os6HsdE=","lUIHwdjN0WX5pTX3QjKoAJywZOtft8jCaYItP36T0dk=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":26,"leaf_hash":"6YLM+QfJBWKoaHE9vKs2KNkovvrMHstm8qLPVVkVdLY=","aunts":["vrT8QOmnjz5D23Q5VFiTGPqcUuciGAZIC/t0De5jHKA=","dREcY8qqq4drNRq2EoakhGxiKXEMOKLNTyx2eprygAI=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":27,"leaf_hash":"vrT8QOmnjz5D23Q5VFiTGPqcUuciGAZIC/t0De5jHKA=","aunts":["6YLM+QfJBWKoaHE9vKs2KNkovvrMHstm8qLPVVkVdLY=","dREcY8qqq4drNRq2EoakhGxiKXEMOKLNTyx2eprygAI=","L3Tn9xKoFaDgcmA4/L6W3j97aFeayHD6OtTmfiTaup4=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":28,"leaf_hash":"fAtgAsvgcisKSRREAPyp5Wx3DIkTVI3KiZW46IyrS9E=","aunts":["XX9ZmbS4qFV3kxWq/nepAUV9HWgZZTRkgYCF9niPgHQ=","3RkN60amzXVt6bT5LYEiFSlgk9QxFzjSjo1sKwa6EEc=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":29,"leaf_hash":"XX9ZmbS4qFV3kxWq/nepAUV9HWgZZTRkgYCF9niPgHQ=","aunts":["fAtgAsvgcisKSRREAPyp5Wx3DIkTVI3KiZW46IyrS9E=","3RkN60amzXVt6bT5LYEiFSlgk9QxFzjSjo1sKwa6EEc=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":30,"leaf_hash":"V+MgqfXP/Xp6dbPEIyKyCSQRWN+o33ZyokIibkB+qDE=","aunts":["fVymoDyU+rbk0ySwSkrvPE8Lsk2U5pfbZu8D4ZvHnF0=","+p2B7bEGuFuR9PQjmq2hVeu7FMS+JAxf+PjKtAuFi3w=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":31,"leaf_hash":"fVymoDyU+rbk0ySwSkrvPE8Lsk2U5pfbZu8D4ZvHnF0=","aunts":["V+MgqfXP/Xp6dbPEIyKyCSQRWN+o33ZyokIibkB+qDE=","+p2B7bEGuFuR9PQjmq2hVeu7FMS+JAxf+PjKtAuFi3w=","PXSIiBnJG+4P9BhnciRtLf/kS1TWEkTcbxuZlBIOpq0=","YMEM0xz5JwRzkbjcXOSv0m4VVAD6VrE8t4EAPxqbPHE=","WR2MM2QgkMvvfPhR04nm7VzppHNF7OBc3jmVGk0/clo=","kExSnjugX4v3Y5iqNuVDJDK9iSiIT8UEMhLqwZ/NzZQ=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":32,"leaf_hash":"1AarVZQ7Y47h1adoEl9f3O4RqBb1ktkXd2RW/qUR9ho=","aunts":["HdQ93Ga5tmcPqkJxfM3GDXgX2hHSulxHJzGILg6xz1o=","6SS/ezUPPP9Vhmp8kHQv9NLWxAN9eppULCShGetHxtg=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":33,"leaf_hash":"HdQ93Ga5tmcPqkJxfM3GDXgX2hHSulxHJzGILg6xz1o=","aunts":["1AarVZQ7Y47h1adoEl9f3O4RqBb1ktkXd2RW/qUR9ho=","6SS/ezUPPP9Vhmp8kHQv9NLWxAN9eppULCShGetHxtg=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":34,"leaf_hash":"KuOtbviKOmPFTwmWuGJjJUsTmONpK5siQgC+CbpzknA=","aunts":["ByblfMUKeilTtBWaTLVkmBIHNMhkecceGiPSgLa8KY0=","Nv8kesrFhTw7nwDYeOgfvrORHhiv6I+aLl7oZmFEaBw=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":35,"leaf_hash":"ByblfMUKeilTtBWaTLVkmBIHNMhkecceGiPSgLa8KY0=","aunts":["KuOtbviKOmPFTwmWuGJjJUsTmONpK5siQgC+CbpzknA=","Nv8kesrFhTw7nwDYeOgfvrORHhiv6I+aLl7oZmFEaBw=","GhDgT4F0QV+U6cQ9LgPlm7y6Mi+QlV7wJUYimXYiIpU=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":36,"leaf_hash":"uAg7ghc128txB+MPmtveLuOYIF1IWiWOPsFBHuJ2PE4=","aunts":["H6Bsm1vo5LPYh6Cw3OKquRbXrAbMUEd7J4MK6Q/uBKk=","3I20LGLactE5JAKpAcEnrJHltyDE2zbGv6bdXu2KSe8=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]},{"total":512,"index":37,"leaf_hash":"H6Bsm1vo5LPYh6Cw3OKquRbXrAbMUEd7J4MK6Q/uBKk=","aunts":["uAg7ghc128txB+MPmtveLuOYIF1IWiWOPsFBHuJ2PE4=","3I20LGLactE5JAKpAcEnrJHltyDE2zbGv6bdXu2KSe8=","RkVLKRv3NGPHVfrswpb0nJqGsINLRXCqOKqiTnO7+B0=","l+z7joS28ofvr3/KU2OudIRaCzzPkCKVYNhJ/gEidTk=","Lu6Uh4ELlgsHwu8N63fwfncS11hHsW8qYJUqEvnJkg8=","m2m2Ijl9bL6m8equSpLcZ+10v/os9+9phOA9e3QR0rM=","EpaCsKeKdIh9/dYk2ngvEFZ1za2QliYOr8qbCKRFPcI=","M5l1aNVgbcM/Dl06+GhY9ZlmLzju/9Eo1oYRC6qvamo=","HqDZo7+S+qXT4UxGYtWMipNdxcAMGYsOzwvou/HKsqY="]}],"start_row":24,"end_row":37},"namespace_version":0},"root":"kVNDrl+cJ0dFEH0drtaBRk940WNXy8brR+/uO97tBfU=","sub_threshold":64}] \ No newline at end of file diff --git a/blob/testdata/fuzz/FuzzCommitmentProofVerify/582528ddfad69eb5 b/blob/testdata/fuzz/FuzzCommitmentProofVerify/582528ddfad69eb5 new file mode 100644 index 0000000000..a96f5599e6 --- /dev/null +++ b/blob/testdata/fuzz/FuzzCommitmentProofVerify/582528ddfad69eb5 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0") diff --git a/blob/testdata/fuzz/FuzzCommitmentProofVerify/6a2b4b982dc67bf9 b/blob/testdata/fuzz/FuzzCommitmentProofVerify/6a2b4b982dc67bf9 new file mode 100644 index 0000000000..706f79beeb --- /dev/null +++ b/blob/testdata/fuzz/FuzzCommitmentProofVerify/6a2b4b982dc67bf9 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\"Commitment_proof\":{\"suBtree_root_proofs\":[{\"end\":1}]},\"suB_threshold\":1}") diff --git a/blob/testdata/fuzz/FuzzCommitmentProofVerify/8093511184ad3e25 b/blob/testdata/fuzz/FuzzCommitmentProofVerify/8093511184ad3e25 new file mode 100644 index 0000000000..3f1f65eca8 --- /dev/null +++ b/blob/testdata/fuzz/FuzzCommitmentProofVerify/8093511184ad3e25 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{}") diff --git a/blob/testdata/fuzz/FuzzCommitmentProofVerify/c23296029c66526c b/blob/testdata/fuzz/FuzzCommitmentProofVerify/c23296029c66526c new file mode 100644 index 0000000000..27871b2214 --- /dev/null +++ b/blob/testdata/fuzz/FuzzCommitmentProofVerify/c23296029c66526c @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\"Commitment_proof\":{\"row_proof\":{\"proofs\":[{}]}},\"root\":\"0000\",\"suB_threshold\":1}") diff --git a/blob/testdata/fuzz/FuzzCommitmentProofVerify/ea1afcc1fa86d415 b/blob/testdata/fuzz/FuzzCommitmentProofVerify/ea1afcc1fa86d415 new file mode 100644 index 0000000000..9e05fb778d --- /dev/null +++ b/blob/testdata/fuzz/FuzzCommitmentProofVerify/ea1afcc1fa86d415 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\"Commitment_proof\":{\"row_proof\":{\"proofs\":[{}]}},\"suB_threshold\":1}") diff --git a/blob/testdata/fuzz/FuzzCommitmentProofVerify/fa5be9420bbacd9a b/blob/testdata/fuzz/FuzzCommitmentProofVerify/fa5be9420bbacd9a new file mode 100644 index 0000000000..f39ba53119 --- /dev/null +++ b/blob/testdata/fuzz/FuzzCommitmentProofVerify/fa5be9420bbacd9a @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\"Commitment_proof\":{}}") diff --git a/blob/testdata/fuzz/FuzzProofEqual/8093511184ad3e25 b/blob/testdata/fuzz/FuzzProofEqual/8093511184ad3e25 new file mode 100644 index 0000000000..3f1f65eca8 --- /dev/null +++ b/blob/testdata/fuzz/FuzzProofEqual/8093511184ad3e25 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{}") diff --git a/celestia-node.mk b/celestia-node.mk new file mode 100644 index 0000000000..2729acc13b --- /dev/null +++ b/celestia-node.mk @@ -0,0 +1,101 @@ +# Celestia Node Management Rules +# These rules can be included in the main Makefile + +.PHONY: install get-address check-and-fund reset-node light-up node-help + +node-help: + @echo "Celestia Light Node Management Commands:" + @echo "" + @echo "Available targets:" + @echo " node-install - Install celestia node and cel-key binaries" + @echo " get-address - Display the wallet address from cel-key" + @echo " check-and-fund - Check wallet balance and request funds if needed" + @echo " reset-node - Reset node state and update config with latest block height" + @echo " light-arabica-up - Start the Celestia light node" + @echo "" + @echo "Special usage:" + @echo " light-arabica-up options:" + @echo " COMMAND=again - Reset the node before starting" + @echo " CORE_IP= - Use custom IP instead of default validator" + @echo "" + @echo "Examples:" + @echo " make light-arabica-up" + @echo " make light-arabica-up COMMAND=again" + @echo " make light-arabica-up CORE_IP=custom.ip.address" + @echo " make light-arabica-up COMMAND=again CORE_IP=custom.ip.address" + +# Install celestia node and cel-key binaries +node-install: + make install + make cel-key + +# Get wallet address from cel-key +get-address: + @address=$$(cel-key list --node.type light --p2p.network arabica | grep "address: " | cut -d' ' -f3); \ + echo $$address + +# Check balance and fund if needed +check-and-fund: + @address=$$(cel-key list --node.type light --p2p.network arabica | grep "address: " | cut -d' ' -f3); \ + echo "Checking balance for address: $$address"; \ + balance=$$(curl -s "https://api.celestia-arabica-11.com/cosmos/bank/v1beta1/balances/$$address" | jq -r '.balances[] | select(.denom == "utia") | .amount // "0"'); \ + if [[ $$balance =~ ^[0-9]+$$ ]]; then \ + balance_tia=$$(echo "scale=6; $$balance/1000000" | bc); \ + echo "Current balance: $$balance_tia TIA"; \ + else \ + balance_tia=0; \ + fi; \ + if (( $$(echo "$$balance_tia < 1" | bc -l) )); then \ + echo "Balance too low. Requesting funds from faucet..."; \ + curl -X POST 'https://faucet.celestia-arabica-11.com/api/v1/faucet/give_me' \ + -H 'Content-Type: application/json' \ + -d '{"address": "'$$address'", "chainId": "arabica-11" }'; \ + echo "Waiting 10 seconds for transaction to process..."; \ + sleep 10; \ + fi + +# Reset node state and update config +reset-node: + @echo "Resetting node state..." + @celestia light unsafe-reset-store --p2p.network arabica + @echo "Getting latest block height and hash..." + @block_response=$$(curl -s https://rpc.celestia-arabica-11.com/block); \ + latest_block=$$(echo $$block_response | jq -r '.result.block.header.height'); \ + latest_hash=$$(echo $$block_response | jq -r '.result.block_id.hash'); \ + echo "Latest block height: $$latest_block"; \ + echo "Latest block hash: $$latest_hash"; \ + config_file="$$HOME/.celestia-light-arabica-11/config.toml"; \ + echo "Updating config.toml..."; \ + sed -i.bak -e "s/\(TrustedHash[[:space:]]*=[[:space:]]*\).*/\1\"$$latest_hash\"/" \ + -e "s/\(SampleFrom[[:space:]]*=[[:space:]]*\).*/\1$$latest_block/" \ + "$$config_file"; \ + echo "Configuration updated successfully" + +# Start the Celestia light node +# Usage: make light-arabica-up [COMMAND=again] [CORE_IP=custom_ip] +light-arabica-up: + @config_file="$$HOME/.celestia-light-arabica-11/config.toml"; \ + if [ "$(COMMAND)" = "again" ]; then \ + $(MAKE) reset-node; \ + fi; \ + if [ -e "$$config_file" ]; then \ + echo "Using config file: $$config_file"; \ + else \ + celestia light init --p2p.network arabica; \ + $(MAKE) reset-node; \ + $(MAKE) check-and-fund; \ + fi; \ + $(MAKE) check-and-fund; \ + if [ -n "$(CORE_IP)" ]; then \ + celestia light start \ + --core.ip $(CORE_IP) \ + --rpc.skip-auth \ + --rpc.addr 0.0.0.0 \ + --p2p.network arabica; \ + else \ + celestia light start \ + --core.ip validator-1.celestia-arabica-11.com \ + --rpc.skip-auth \ + --rpc.addr 0.0.0.0 \ + --p2p.network arabica; \ + fi diff --git a/cmd/auth.go b/cmd/auth.go index b29497d487..2f4fa37d38 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "path/filepath" + "time" "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" @@ -19,6 +20,8 @@ import ( nodemod "github.com/celestiaorg/celestia-node/nodebuilder/node" ) +var ttlFlagName = "ttl" + func AuthCmd(fsets ...*flag.FlagSet) *cobra.Command { cmd := &cobra.Command{ Use: "auth [permission-level (e.g. read || write || admin)]", @@ -34,6 +37,11 @@ func AuthCmd(fsets ...*flag.FlagSet) *cobra.Command { return err } + ttl, err := cmd.Flags().GetDuration(ttlFlagName) + if err != nil { + return err + } + ks, err := newKeystore(StorePath(cmd.Context())) if err != nil { return err @@ -50,7 +58,7 @@ func AuthCmd(fsets ...*flag.FlagSet) *cobra.Command { } } - token, err := buildJWTToken(key.Body, permissions) + token, err := buildJWTToken(key.Body, permissions, ttl) if err != nil { return err } @@ -62,6 +70,8 @@ func AuthCmd(fsets ...*flag.FlagSet) *cobra.Command { for _, set := range fsets { cmd.Flags().AddFlagSet(set) } + cmd.Flags().Duration(ttlFlagName, 0, "Set a Time-to-live (TTL) for the token") + return cmd } @@ -73,12 +83,12 @@ func newKeystore(path string) (keystore.Keystore, error) { return keystore.NewFSKeystore(filepath.Join(expanded, "keys"), nil) } -func buildJWTToken(body []byte, permissions []auth.Permission) (string, error) { +func buildJWTToken(body []byte, permissions []auth.Permission, ttl time.Duration) (string, error) { signer, err := jwt.NewSignerHS(jwt.HS256, body) if err != nil { return "", err } - return authtoken.NewSignedJWT(signer, permissions) + return authtoken.NewSignedJWT(signer, permissions, ttl) } func generateNewKey(ks keystore.Keystore) (keystore.PrivKey, error) { diff --git a/cmd/cel-key/main.go b/cmd/cel-key/main.go index 5db3f4bc7c..33d3f1487a 100644 --- a/cmd/cel-key/main.go +++ b/cmd/cel-key/main.go @@ -12,8 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/spf13/cobra" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" ) var encodingConfig = encoding.MakeConfig(app.ModuleEncodingRegisters...) diff --git a/cmd/cel-key/node_types.go b/cmd/cel-key/node_types.go index a70889a263..470ef596e7 100644 --- a/cmd/cel-key/node_types.go +++ b/cmd/cel-key/node_types.go @@ -10,6 +10,7 @@ import ( flag "github.com/spf13/pflag" "github.com/celestiaorg/celestia-node/nodebuilder" + nodemod "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" ) @@ -42,32 +43,27 @@ func ParseDirectoryFlags(cmd *cobra.Command) error { return nil } - nodeType := cmd.Flag(nodeDirKey).Value.String() - if nodeType == "" { - return errors.New("no node type provided") + nodeTypeStr := cmd.Flag(nodeDirKey).Value.String() + nodeType := nodemod.ParseType(nodeTypeStr) + if !nodeType.IsValid() { + return errors.New("no or invalid node type provided") } - network := cmd.Flag(networkKey).Value.String() - if net, err := p2p.Network(network).Validate(); err == nil { - network = string(net) - } else { - fmt.Println("WARNING: unknown network specified: ", network) + network, err := p2p.ParseNetwork(cmd) + if err != nil { + return err } - switch nodeType { - case "bridge", "full", "light": - path, err := nodebuilder.DefaultNodeStorePath(nodeType, network) - if err != nil { - return err - } - keyPath := fmt.Sprintf("%s/keys", path) - fmt.Println("using directory: ", keyPath) - if err := cmd.Flags().Set(sdkflags.FlagKeyringDir, keyPath); err != nil { - return err - } - default: - return fmt.Errorf("node type %s is not valid. Provided node types: (bridge || full || light)", - nodeType) + path, err := nodebuilder.DefaultNodeStorePath(nodeType, network) + if err != nil { + return err } + + keyPath := fmt.Sprintf("%s/keys", path) + fmt.Println("using directory: ", keyPath) + if err := cmd.Flags().Set(sdkflags.FlagKeyringDir, keyPath); err != nil { + return err + } + return nil } diff --git a/cmd/cel-shed/eds_store_stress.go b/cmd/cel-shed/eds_store_stress.go index 9036a81e30..1150686a03 100644 --- a/cmd/cel-shed/eds_store_stress.go +++ b/cmd/cel-shed/eds_store_stress.go @@ -23,6 +23,7 @@ import ( const ( edsStorePathFlag = "path" edsWritesFlag = "writes" + edsWriteFrom = "init_height" edsSizeFlag = "size" edsDisableLogFlag = "disable-log" edsLogStatFreqFlag = "log-stat-freq" @@ -119,12 +120,14 @@ var edsStoreStress = &cobra.Command{ disableLog, _ := cmd.Flags().GetBool(edsDisableLogFlag) logFreq, _ := cmd.Flags().GetInt(edsLogStatFreqFlag) edsWrites, _ := cmd.Flags().GetInt(edsWritesFlag) + writeFrom, _ := cmd.Flags().GetInt(edsWriteFrom) edsSize, _ := cmd.Flags().GetInt(edsSizeFlag) putTimeout, _ := cmd.Flags().GetInt(putTimeoutFlag) cfg := edssser.Config{ EDSSize: edsSize, EDSWrites: edsWrites, + WriteFrom: writeFrom, EnableLog: !disableLog, LogFilePath: path, StatLogFreq: logFreq, @@ -144,12 +147,7 @@ var edsStoreStress = &cobra.Command{ err = errors.Join(err, nodestore.Close()) }() - datastore, err := nodestore.Datastore() - if err != nil { - return err - } - - stresser, err := edssser.NewEDSsser(path, datastore, cfg) + stresser, err := edssser.NewEDSsser(path, cfg) if err != nil { return err } diff --git a/cmd/cel-shed/main.go b/cmd/cel-shed/main.go index 872bbb48a9..f54b50e886 100644 --- a/cmd/cel-shed/main.go +++ b/cmd/cel-shed/main.go @@ -10,7 +10,7 @@ import ( ) func init() { - rootCmd.AddCommand(p2pCmd, headerCmd, edsStoreCmd) + rootCmd.AddCommand(p2pCmd, headerCmd, edsStoreCmd, shwapCmd) } var rootCmd = &cobra.Command{ diff --git a/cmd/cel-shed/p2p.go b/cmd/cel-shed/p2p.go index 957582c8bc..df213145db 100644 --- a/cmd/cel-shed/p2p.go +++ b/cmd/cel-shed/p2p.go @@ -1,17 +1,28 @@ package main import ( + "context" "crypto/rand" "encoding/hex" "fmt" + "os" + "sync" + "sync/atomic" + "time" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/spf13/cobra" + "go.uber.org/fx" + + "github.com/celestiaorg/celestia-node/nodebuilder" + "github.com/celestiaorg/celestia-node/nodebuilder/fraud" + "github.com/celestiaorg/celestia-node/nodebuilder/node" + "github.com/celestiaorg/celestia-node/nodebuilder/p2p" ) func init() { - p2pCmd.AddCommand(p2pNewKeyCmd, p2pPeerIDCmd) + p2pCmd.AddCommand(p2pNewKeyCmd, p2pPeerIDCmd, p2pConnectBootstrappersCmd) } var p2pCmd = &cobra.Command{ @@ -75,3 +86,119 @@ var p2pPeerIDCmd = &cobra.Command{ }, Args: cobra.ExactArgs(1), } + +var ( + errorOnAnyFailure bool + errorOnAllFailure bool + connectionTimeout time.Duration +) + +var p2pConnectBootstrappersCmd = &cobra.Command{ + Use: "connect-bootstrappers [network]", + Short: "Connect to bootstrappers of a certain network", + RunE: func(cmd *cobra.Command, args []string) error { + if errorOnAnyFailure && errorOnAllFailure { + return fmt.Errorf("only one of --err-any and --err-all can be specified") + } + + ctx, cancel := context.WithTimeout(cmd.Context(), connectionTimeout) + defer cancel() + + network := p2p.GetNetwork(args[0]) + bootstrappers, err := p2p.BootstrappersFor(network) + if err != nil { + return fmt.Errorf("failed to get bootstrappers: %w", err) + } + + store := nodebuilder.NewMemStore() + cfg := p2p.DefaultConfig(node.Light) + modp2p := p2p.ConstructModule(node.Light, &cfg) + + var mod p2p.Module + app := fx.New( + fx.NopLogger, + modp2p, + fx.Provide(fraud.Unmarshaler), + fx.Provide(cmd.Context), + fx.Provide(store.Keystore), + fx.Provide(store.Datastore), + fx.Supply(bootstrappers), + fx.Supply(network), + fx.Supply(node.Light), + fx.Invoke(func(modprov p2p.Module) { + mod = modprov + }), + ) + + if err := app.Start(ctx); err != nil { + return fmt.Errorf("failed to start app: %w", err) + } + defer func() { + if err := app.Stop(ctx); err != nil { + fmt.Printf("failed to stop application: %v\n", err) + } + }() + + p2pInfo, err := mod.Info(ctx) + if err != nil { + return fmt.Errorf("failed to get p2p info: %w", err) + } + + fmt.Printf("PeerID: %s\n", p2pInfo.ID) + for _, addr := range p2pInfo.Addrs { + fmt.Printf("Listening on: %s\n", addr.String()) + } + fmt.Println() + + var successfulConnections atomic.Int32 + var failedConnections atomic.Int32 + var wg sync.WaitGroup + + for _, bootstrapper := range bootstrappers { + wg.Add(1) + go func(bootstrapper peer.AddrInfo) { + defer wg.Done() + fmt.Printf("Attempting to connect to bootstrapper: %s\n", bootstrapper) + if err := mod.Connect(ctx, bootstrapper); err != nil { + fmt.Printf("Error: Failed to connect to bootstrapper %s. Reason: %v\n", bootstrapper, err) + failedConnections.Add(1) + return + } + fmt.Printf("Success: Connected to bootstrapper: %s\n", bootstrapper) + successfulConnections.Add(1) + }(bootstrapper) + } + + wg.Wait() + + if failedConnections.Load() == int32(len(bootstrappers)) && errorOnAllFailure { + fmt.Println() + fmt.Println("failed to connect to all bootstrappers") + os.Exit(1) + return nil + } else if failedConnections.Load() > 0 && errorOnAnyFailure { + fmt.Println() + fmt.Println("failed to connect to some bootstrappers") + os.Exit(1) + return nil + } + + return nil + }, + Args: cobra.ExactArgs(1), +} + +func init() { + p2pConnectBootstrappersCmd.Flags().BoolVar( + &errorOnAnyFailure, "err-any", false, + "Return error if at least one bootstrapper is not reachable", + ) + p2pConnectBootstrappersCmd.Flags().BoolVar( + &errorOnAllFailure, "err-all", false, + "Return error if no bootstrapper is reachable", + ) + p2pConnectBootstrappersCmd.Flags().DurationVar( + &connectionTimeout, "timeout", 10*time.Second, + "Timeout duration for the entire bootstrapper connection process", + ) +} diff --git a/cmd/cel-shed/shwap.go b/cmd/cel-shed/shwap.go new file mode 100644 index 0000000000..2d88e59ca9 --- /dev/null +++ b/cmd/cel-shed/shwap.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "reflect" + + "github.com/ipfs/go-cid" + "github.com/spf13/cobra" + + "github.com/celestiaorg/celestia-node/share/shwap/p2p/bitswap" +) + +func init() { + shwapCmd.AddCommand(shwapCIDType) +} + +var shwapCmd = &cobra.Command{ + Use: "shwap [subcommand]", + Short: "Collection of shwap related utilities", +} + +var shwapCIDType = &cobra.Command{ + Use: "cid-type", + Short: "Decodes Bitswap CID composed over Shwap CID", + RunE: func(_ *cobra.Command, args []string) error { + cid, err := cid.Decode(args[0]) + if err != nil { + return fmt.Errorf("decoding cid: %w", err) + } + + blk, err := bitswap.EmptyBlock(cid) + if err != nil { + return fmt.Errorf("building block: %w", err) + } + + fmt.Printf("%s: %+v\n", reflect.TypeOf(blk), blk) + return nil + }, + Args: cobra.ExactArgs(1), +} diff --git a/cmd/flags_misc.go b/cmd/flags_misc.go index 332d2f8022..93c56ce8ca 100644 --- a/cmd/flags_misc.go +++ b/cmd/flags_misc.go @@ -149,12 +149,12 @@ func ParseMiscFlags(ctx context.Context, cmd *cobra.Command) (context.Context, e } } - ok, err := cmd.Flags().GetBool(pprofFlag) + enablePprof, err := cmd.Flags().GetBool(pprofFlag) if err != nil { panic(err) } - if ok { + if enablePprof { // TODO(@Wondertan): Eventually, this should be registered on http server in RPC // by passing the http.Server with preregistered pprof handlers to the node. // Node should not register pprof itself. @@ -174,12 +174,12 @@ func ParseMiscFlags(ctx context.Context, cmd *cobra.Command) (context.Context, e }() } - ok, err = cmd.Flags().GetBool(pyroscopeFlag) + enablePyro, err := cmd.Flags().GetBool(pyroscopeFlag) if err != nil { panic(err) } - if ok { + if enablePyro { ctx = WithNodeOptions(ctx, nodebuilder.WithPyroscope( cmd.Flag(pyroscopeEndpoint).Value.String(), @@ -188,12 +188,12 @@ func ParseMiscFlags(ctx context.Context, cmd *cobra.Command) (context.Context, e ) } - ok, err = cmd.Flags().GetBool(tracingFlag) + enableTracing, err := cmd.Flags().GetBool(tracingFlag) if err != nil { panic(err) } - if ok { + if enableTracing { opts := []otlptracehttp.Option{ otlptracehttp.WithCompression(otlptracehttp.GzipCompression), otlptracehttp.WithEndpoint(cmd.Flag(tracingEndpointFlag).Value.String()), @@ -205,11 +205,11 @@ func ParseMiscFlags(ctx context.Context, cmd *cobra.Command) (context.Context, e } pyroOpts := make([]otelpyroscope.Option, 0) - ok, err = cmd.Flags().GetBool(pyroscopeTracing) + enablePyroTracing, err := cmd.Flags().GetBool(pyroscopeTracing) if err != nil { panic(err) } - if ok { + if enablePyroTracing { pyroOpts = append(pyroOpts, otelpyroscope.WithAppName("celestia.da-node"), otelpyroscope.WithPyroscopeURL(cmd.Flag(pyroscopeEndpoint).Value.String()), @@ -222,12 +222,12 @@ func ParseMiscFlags(ctx context.Context, cmd *cobra.Command) (context.Context, e ctx = WithNodeOptions(ctx, nodebuilder.WithTraces(opts, pyroOpts)) } - ok, err = cmd.Flags().GetBool(metricsFlag) + enableMetrics, err := cmd.Flags().GetBool(metricsFlag) if err != nil { panic(err) } - if ok { + if enableMetrics { opts := []otlpmetrichttp.Option{ otlpmetrichttp.WithCompression(otlpmetrichttp.GzipCompression), otlpmetrichttp.WithEndpoint(cmd.Flag(metricsEndpointFlag).Value.String()), @@ -241,13 +241,13 @@ func ParseMiscFlags(ctx context.Context, cmd *cobra.Command) (context.Context, e ctx = WithNodeOptions(ctx, nodebuilder.WithMetrics(opts, NodeType(ctx))) } - ok, err = cmd.Flags().GetBool(p2pMetrics) + enablep2pMetrics, err := cmd.Flags().GetBool(p2pMetrics) if err != nil { panic(err) } - if ok { - if metricsEnabled, _ := cmd.Flags().GetBool(metricsFlag); !metricsEnabled { + if enablep2pMetrics { + if !enableMetrics { log.Error("--p2p.metrics used without --metrics being enabled") } else { ctx = WithNodeOptions(ctx, modp2p.WithMetrics()) diff --git a/cmd/flags_node.go b/cmd/flags_node.go index bc86cb4a27..a6736f8e85 100644 --- a/cmd/flags_node.go +++ b/cmd/flags_node.go @@ -42,7 +42,7 @@ func ParseNodeFlags(ctx context.Context, cmd *cobra.Command, network p2p.Network if store == "" { tp := NodeType(ctx) var err error - store, err = nodebuilder.DefaultNodeStorePath(tp.String(), network.String()) + store, err = nodebuilder.DefaultNodeStorePath(tp, network) if err != nil { return ctx, err } diff --git a/cmd/rpc.go b/cmd/rpc.go index 230b51508b..66a3ee6df0 100644 --- a/cmd/rpc.go +++ b/cmd/rpc.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "path/filepath" + "time" "github.com/spf13/cobra" flag "github.com/spf13/pflag" @@ -18,6 +19,7 @@ import ( var ( requestURL string authTokenFlag string + timeoutFlag time.Duration ) func RPCFlags() *flag.FlagSet { @@ -37,6 +39,13 @@ func RPCFlags() *flag.FlagSet { "Authorization token", ) + fset.DurationVar( + &timeoutFlag, + "timeout", + 0, + "Timeout for RPC requests (e.g. 30s, 1m)", + ) + storeFlag := NodeFlags().Lookup(nodeStoreFlag) fset.AddFlag(storeFlag) return fset @@ -73,6 +82,12 @@ func InitClient(cmd *cobra.Command, _ []string) error { } } + if timeoutFlag > 0 { + // we don't cancel, because we want to keep this context alive outside the InitClient Function + ctx, _ := context.WithTimeout(cmd.Context(), timeoutFlag) //nolint:govet + cmd.SetContext(ctx) + } + client, err := rpc.NewClient(cmd.Context(), requestURL, authTokenFlag) if err != nil { return err @@ -113,7 +128,7 @@ func getToken(path string) (string, error) { fmt.Printf("error getting the JWT secret: %v", err) return "", err } - return buildJWTToken(key.Body, perms.AllPerms) + return buildJWTToken(key.Body, perms.AllPerms, 0) } type rpcClientKey struct{} diff --git a/cmd/start.go b/cmd/start.go index 85970cbd6e..3a71c5a6db 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -10,8 +10,8 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/spf13/cobra" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" "github.com/celestiaorg/celestia-node/nodebuilder" ) diff --git a/cmd/util.go b/cmd/util.go index 750b9d9ae5..026002c1d6 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -11,6 +11,8 @@ import ( "github.com/spf13/cobra" flag "github.com/spf13/pflag" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/nodebuilder/core" "github.com/celestiaorg/celestia-node/nodebuilder/gateway" "github.com/celestiaorg/celestia-node/nodebuilder/header" @@ -19,7 +21,6 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/pruner" rpc_cfg "github.com/celestiaorg/celestia-node/nodebuilder/rpc" "github.com/celestiaorg/celestia-node/nodebuilder/state" - "github.com/celestiaorg/celestia-node/share" ) func PrintOutput(data interface{}, err error, formatData func(interface{}) interface{}) error { @@ -47,14 +48,14 @@ func PrintOutput(data interface{}, err error, formatData func(interface{}) inter // ParseV0Namespace parses a namespace from a base64 or hex string. The param // is expected to be the user-specified portion of a v0 namespace ID (i.e. the // last 10 bytes). -func ParseV0Namespace(param string) (share.Namespace, error) { +func ParseV0Namespace(param string) (libshare.Namespace, error) { userBytes, err := DecodeToBytes(param) if err != nil { - return nil, err + return libshare.Namespace{}, err } // if the namespace ID is <= 10 bytes, left pad it with 0s - return share.NewBlobNamespaceV0(userBytes) + return libshare.NewV0Namespace(userBytes) } // DecodeToBytes decodes a Base64 or hex input string into a byte slice. @@ -115,7 +116,7 @@ func PersistentPreRunEnv(cmd *cobra.Command, nodeType node.Type, _ []string) err rpc_cfg.ParseFlags(cmd, &cfg.RPC) gateway.ParseFlags(cmd, &cfg.Gateway) - pruner.ParseFlags(cmd, &cfg.Pruner) + pruner.ParseFlags(cmd, &cfg.Pruner, nodeType) switch nodeType { case node.Light, node.Full: err = header.ParseFlags(cmd, &cfg.Header) diff --git a/cmd/util_test.go b/cmd/util_test.go index b6e245f3e2..68464ec32d 100644 --- a/cmd/util_test.go +++ b/cmd/util_test.go @@ -4,22 +4,23 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-node/share" + libshare "github.com/celestiaorg/go-square/v2/share" ) func Test_parseNamespaceID(t *testing.T) { type testCase struct { name string param string - want share.Namespace + want []byte wantErr bool } testCases := []testCase{ { param: "0x0c204d39600fddd3", name: "8 byte hex encoded namespace ID gets left padded", - want: share.Namespace{ + want: []byte{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3, }, @@ -28,7 +29,7 @@ func Test_parseNamespaceID(t *testing.T) { { name: "10 byte hex encoded namespace ID", param: "0x42690c204d39600fddd3", - want: share.Namespace{ + want: []byte{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x69, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3, }, @@ -37,7 +38,7 @@ func Test_parseNamespaceID(t *testing.T) { { name: "29 byte hex encoded namespace ID", param: "0x0000000000000000000000000000000000000001010101010101010101", - want: share.Namespace{ + want: []byte{ 0x0, // namespace version 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // v0 ID prefix 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // namespace ID @@ -47,13 +48,13 @@ func Test_parseNamespaceID(t *testing.T) { { name: "11 byte hex encoded namespace ID returns error", param: "0x42690c204d39600fddd3a3", - want: share.Namespace{}, + want: nil, wantErr: true, }, { name: "10 byte base64 encoded namespace ID", param: "QmkMIE05YA/d0w==", - want: share.Namespace{ + want: []byte{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x69, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3, }, @@ -62,7 +63,7 @@ func Test_parseNamespaceID(t *testing.T) { { name: "not base64 or hex encoded namespace ID returns error", param: "5748493939429", - want: share.Namespace{}, + want: nil, wantErr: true, }, } @@ -74,8 +75,11 @@ func Test_parseNamespaceID(t *testing.T) { assert.Error(t, err) return } + assert.NoError(t, err) - assert.Equal(t, tc.want, got) + ns, err := libshare.NewNamespaceFromBytes(tc.want) + require.NoError(t, err) + assert.Equal(t, ns, got) }) } } diff --git a/core/eds.go b/core/eds.go index bb53178e09..2e8ce7ea19 100644 --- a/core/eds.go +++ b/core/eds.go @@ -2,37 +2,35 @@ package core import ( "context" - "errors" "fmt" + "time" - "github.com/filecoin-project/dagstore" "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" - "github.com/celestiaorg/go-square/shares" - "github.com/celestiaorg/go-square/square" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libsquare "github.com/celestiaorg/go-square/v2" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/pruner" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/share/availability" + "github.com/celestiaorg/celestia-node/store" ) // extendBlock extends the given block data, returning the resulting // ExtendedDataSquare (EDS). If there are no transactions in the block, // nil is returned in place of the eds. func extendBlock(data types.Data, appVersion uint64, options ...nmt.Option) (*rsmt2d.ExtendedDataSquare, error) { - if app.IsEmptyBlock(data, appVersion) { - return share.EmptyExtendedDataSquare(), nil + if app.IsEmptyBlockRef(&data, appVersion) { + return share.EmptyEDS(), nil } // Construct the data square from the block's transactions - dataSquare, err := square.Construct( + square, err := libsquare.Construct( data.Txs.ToSliceOfBytes(), appconsts.SquareSizeUpperBound(appVersion), appconsts.SubtreeRootThreshold(appVersion), @@ -40,17 +38,17 @@ func extendBlock(data types.Data, appVersion uint64, options ...nmt.Option) (*rs if err != nil { return nil, err } - return extendShares(shares.ToBytes(dataSquare), options...) + return extendShares(libshare.ToBytes(square), options...) } func extendShares(s [][]byte, options ...nmt.Option) (*rsmt2d.ExtendedDataSquare, error) { // Check that the length of the square is a power of 2. - if !shares.IsPowerOfTwo(len(s)) { + if !libsquare.IsPowerOfTwo(len(s)) { return nil, fmt.Errorf("number of shares is not a power of 2: got %d", len(s)) } // here we construct a tree // Note: uses the nmt wrapper to construct the tree. - squareSize := square.Size(len(s)) + squareSize := libsquare.Size(len(s)) return rsmt2d.ComputeExtendedDataSquare(s, appconsts.DefaultCodec(), wrapper.NewConstructor(uint64(squareSize), @@ -62,25 +60,21 @@ func storeEDS( ctx context.Context, eh *header.ExtendedHeader, eds *rsmt2d.ExtendedDataSquare, - adder *ipld.ProofsAdder, - store *eds.Store, - window pruner.AvailabilityWindow, + store *store.Store, + window time.Duration, + archival bool, ) error { - if eds.Equals(share.EmptyExtendedDataSquare()) { - return nil - } - - if !pruner.IsWithinAvailabilityWindow(eh.Time(), window) { + if !archival && !availability.IsWithinWindow(eh.Time(), window) { log.Debugw("skipping storage of historic block", "height", eh.Height()) return nil } - ctx = ipld.CtxWithProofsAdder(ctx, adder) - - err := store.Put(ctx, share.DataHash(eh.DataHash), eds) - if errors.Is(err, dagstore.ErrShardExists) { - // block with given root already exists, return nil - return nil + var err error + // archival nodes should not store Q4 outside the availability window. + if availability.IsWithinWindow(eh.Time(), availability.StorageWindow) { + err = store.PutODSQ4(ctx, eh.DAH, eh.Height(), eds) + } else { + err = store.PutODS(ctx, eh.DAH, eh.Height(), eds) } if err == nil { log.Debugw("stored EDS for height", "height", eh.Height()) diff --git a/core/eds_test.go b/core/eds_test.go index f6df18c4be..3bed7ea94e 100644 --- a/core/eds_test.go +++ b/core/eds_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" "github.com/celestiaorg/celestia-node/share" ) @@ -24,11 +24,11 @@ func TestTrulyEmptySquare(t *testing.T) { eds, err := extendBlock(data, appconsts.LatestVersion) require.NoError(t, err) - require.True(t, eds.Equals(share.EmptyExtendedDataSquare())) + require.True(t, eds.Equals(share.EmptyEDS())) } -// TestEmptySquareWithZeroTxs tests that the DAH hash of a block with no transactions -// is equal to the DAH hash for an empty root even if SquareSize is set to +// TestEmptySquareWithZeroTxs tests that the datahash of a block with no transactions +// is equal to the datahash of an empty eds, even if SquareSize is set to // something non-zero. Technically, this block data is invalid because the // construction of the square is deterministic, and the rules which dictate the // square size do not allow for empty block data. However, should that ever @@ -40,13 +40,13 @@ func TestEmptySquareWithZeroTxs(t *testing.T) { eds, err := extendBlock(data, appconsts.LatestVersion) require.NoError(t, err) - require.True(t, eds.Equals(share.EmptyExtendedDataSquare())) + require.True(t, eds.Equals(share.EmptyEDS())) // force extend the square using an empty block and compare with the min DAH eds, err = app.ExtendBlock(data, appconsts.LatestVersion) require.NoError(t, err) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - assert.Equal(t, share.EmptyRoot().Hash(), dah.Hash()) + assert.Equal(t, share.EmptyEDSRoots().Hash(), roots.Hash()) } diff --git a/core/exchange.go b/core/exchange.go index 6593111e5f..372906ff85 100644 --- a/core/exchange.go +++ b/core/exchange.go @@ -6,32 +6,31 @@ import ( "fmt" "time" + "github.com/tendermint/tendermint/types" "golang.org/x/sync/errgroup" libhead "github.com/celestiaorg/go-header" - "github.com/celestiaorg/nmt" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/pruner" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/store" ) -const concurrencyLimit = 4 +const concurrencyLimit = 16 type Exchange struct { fetcher *BlockFetcher - store *eds.Store + store *store.Store construct header.ConstructFn - availabilityWindow pruner.AvailabilityWindow + availabilityWindow time.Duration + archival bool metrics *exchangeMetrics } func NewExchange( fetcher *BlockFetcher, - store *eds.Store, + store *store.Store, construct header.ConstructFn, opts ...Option, ) (*Exchange, error) { @@ -56,6 +55,7 @@ func NewExchange( store: store, construct: construct, availabilityWindow: p.availabilityWindow, + archival: p.archival, metrics: metrics, }, nil } @@ -82,7 +82,7 @@ func (ce *Exchange) GetRangeByHeight( ce.metrics.requestDurationPerHeader(ctx, time.Since(start), amount) for _, h := range headers { - err := libhead.Verify[*header.ExtendedHeader](from, h, libhead.DefaultHeightThreshold) + err := libhead.Verify[*header.ExtendedHeader](from, h) if err != nil { return nil, fmt.Errorf("verifying next header against last verified height: %d: %w", from.Height(), err) @@ -134,11 +134,7 @@ func (ce *Exchange) Get(ctx context.Context, hash libhead.Hash) (*header.Extende return nil, fmt.Errorf("fetching block info for height %d: %w", &block.Height, err) } - // extend block data - adder := ipld.NewProofsAdder(int(block.Data.SquareSize)) - defer adder.Purge() - - eds, err := extendBlock(block.Data, block.Header.Version.App, nmt.NodeVisitor(adder.VisitFn())) + eds, err := extendBlock(block.Data, block.Header.Version.App) if err != nil { return nil, fmt.Errorf("extending block data for height %d: %w", &block.Height, err) } @@ -153,7 +149,7 @@ func (ce *Exchange) Get(ctx context.Context, hash libhead.Hash) (*header.Extende &block.Height, hash, eh.Hash()) } - err = storeEDS(ctx, eh, eds, adder, ce.store, ce.availabilityWindow) + err = storeEDS(ctx, eh, eds, ce.store, ce.availabilityWindow, ce.archival) if err != nil { return nil, err } @@ -179,21 +175,21 @@ func (ce *Exchange) getExtendedHeaderByHeight(ctx context.Context, height *int64 } log.Debugw("fetched signed block from core", "height", b.Header.Height) - // extend block data - adder := ipld.NewProofsAdder(int(b.Data.SquareSize)) - defer adder.Purge() - - eds, err := extendBlock(b.Data, b.Header.Version.App, nmt.NodeVisitor(adder.VisitFn())) + eds, err := extendBlock(b.Data, b.Header.Version.App) if err != nil { return nil, fmt.Errorf("extending block data for height %d: %w", b.Header.Height, err) } - // create extended header + + // TODO(@Wondertan): This is a hack to deref Data, allowing GC to pick it up. + // The better footgun-less solution is to change core.ResultSignedBlock fields to be pointers instead of values. + b.Data = types.Data{} + eh, err := ce.construct(&b.Header, &b.Commit, &b.ValidatorSet, eds) if err != nil { panic(fmt.Errorf("constructing extended header for height %d: %w", b.Header.Height, err)) } - err = storeEDS(ctx, eh, eds, adder, ce.store, ce.availabilityWindow) + err = storeEDS(ctx, eh, eds, ce.store, ce.availabilityWindow, ce.archival) if err != nil { return nil, err } diff --git a/core/exchange_test.go b/core/exchange_test.go index fc43121425..a2187ed7c8 100644 --- a/core/exchange_test.go +++ b/core/exchange_test.go @@ -7,17 +7,14 @@ import ( "time" "github.com/cosmos/cosmos-sdk/client/flags" - ds "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/test/util/testnode" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/pruner" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/store" ) func TestCoreExchange_RequestHeaders(t *testing.T) { @@ -29,7 +26,8 @@ func TestCoreExchange_RequestHeaders(t *testing.T) { generateNonEmptyBlocks(t, ctx, fetcher, cfg, cctx) - store := createStore(t) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) ce, err := NewExchange(fetcher, store, header.MakeExtendedHeader) require.NoError(t, err) @@ -55,7 +53,11 @@ func TestCoreExchange_RequestHeaders(t *testing.T) { assert.Equal(t, expectedLastHeightInRange, headers[len(headers)-1].Height()) for _, h := range headers { - has, err := store.Has(ctx, h.DAH.Hash()) + has, err := store.HasByHash(ctx, h.DAH.Hash()) + require.NoError(t, err) + assert.True(t, has) + + has, err = store.HasByHeight(ctx, h.Height()) require.NoError(t, err) assert.True(t, has) } @@ -72,13 +74,14 @@ func TestExchange_DoNotStoreHistoric(t *testing.T) { generateNonEmptyBlocks(t, ctx, fetcher, cfg, cctx) - store := createStore(t) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) ce, err := NewExchange( fetcher, store, header.MakeExtendedHeader, - WithAvailabilityWindow(pruner.AvailabilityWindow(time.Nanosecond)), // all blocks will be "historic" + WithAvailabilityWindow(time.Nanosecond), // all blocks will be "historic" ) require.NoError(t, err) @@ -94,48 +97,76 @@ func TestExchange_DoNotStoreHistoric(t *testing.T) { // ensure none of the "historic" EDSs were stored for _, h := range headers { - if bytes.Equal(h.DataHash, share.EmptyRoot().Hash()) { + has, err := store.HasByHeight(ctx, h.Height()) + require.NoError(t, err) + assert.False(t, has) + + // empty EDSs are expected to exist in the store, so we skip them + if h.DAH.Equals(share.EmptyEDSRoots()) { continue } - has, err := store.Has(ctx, h.DAH.Hash()) + has, err = store.HasByHash(ctx, h.DAH.Hash()) require.NoError(t, err) assert.False(t, has) } } -func createCoreFetcher(t *testing.T, cfg *testnode.Config) (*BlockFetcher, testnode.Context) { - cctx := StartTestNodeWithConfig(t, cfg) - // wait for height 2 in order to be able to start submitting txs (this prevents - // flakiness with accessing account state) - _, err := cctx.WaitForHeightWithTimeout(2, time.Second*2) // TODO @renaynay: configure? - require.NoError(t, err) - return NewBlockFetcher(cctx.Client), cctx -} +// TestExchange_StoreHistoricIfArchival makes sure blocks are stored past +// sampling window if archival is enabled +func TestExchange_StoreHistoricIfArchival(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) -func createStore(t *testing.T) *eds.Store { - t.Helper() + cfg := DefaultTestConfig() + fetcher, cctx := createCoreFetcher(t, cfg) - storeCfg := eds.DefaultParameters() - store, err := eds.NewStore(storeCfg, t.TempDir(), ds_sync.MutexWrap(ds.NewMapDatastore())) + generateNonEmptyBlocks(t, ctx, fetcher, cfg, cctx) + + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) require.NoError(t, err) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ce, err := NewExchange( + fetcher, + store, + header.MakeExtendedHeader, + WithAvailabilityWindow(time.Nanosecond), // all blocks will be "historic" + WithArchivalMode(), // make sure to store them anyway + ) + require.NoError(t, err) - err = store.Start(ctx) + // initialize store with genesis block + genHeight := int64(1) + genBlock, err := fetcher.GetBlock(ctx, &genHeight) + require.NoError(t, err) + genHeader, err := ce.Get(ctx, genBlock.Header.Hash().Bytes()) require.NoError(t, err) - // store an empty square to initialize EDS store - eds := share.EmptyExtendedDataSquare() - err = store.Put(ctx, share.EmptyRoot().Hash(), eds) + headers, err := ce.GetRangeByHeight(ctx, genHeader, 30) require.NoError(t, err) - t.Cleanup(func() { - err = store.Stop(ctx) + // ensure all "historic" EDSs were stored + for _, h := range headers { + has, err := store.HasByHeight(ctx, h.Height()) require.NoError(t, err) - }) + assert.True(t, has) - return store + // empty EDSs are expected to exist in the store, so we skip them + if h.DAH.Equals(share.EmptyEDSRoots()) { + continue + } + has, err = store.HasByHash(ctx, h.DAH.Hash()) + require.NoError(t, err) + assert.True(t, has) + } +} + +func createCoreFetcher(t *testing.T, cfg *testnode.Config) (*BlockFetcher, testnode.Context) { + cctx := StartTestNodeWithConfig(t, cfg) + // wait for height 2 in order to be able to start submitting txs (this prevents + // flakiness with accessing account state) + _, err := cctx.WaitForHeightWithTimeout(2, time.Second*2) // TODO @renaynay: configure? + require.NoError(t, err) + return NewBlockFetcher(cctx.Client), cctx } // fillBlocks fills blocks until the context is canceled. @@ -152,7 +183,7 @@ func fillBlocks( default: } - _, err := cctx.FillBlock(16, cfg.Genesis.Accounts()[0].Name, flags.BroadcastBlock) + _, err := cctx.FillBlock(16, cfg.Genesis.Accounts()[0].Name, flags.BroadcastAsync) require.NoError(t, err) } } @@ -185,10 +216,11 @@ func generateNonEmptyBlocks( case b, ok := <-sub: require.True(t, ok) - if !bytes.Equal(b.Data.Hash(), share.EmptyRoot().Hash()) { - hashes = append(hashes, share.DataHash(b.Data.Hash())) - i++ + if bytes.Equal(share.EmptyEDSDataHash(), b.Data.Hash()) { + continue } + hashes = append(hashes, share.DataHash(b.Data.Hash())) + i++ case <-ctx.Done(): t.Fatal("failed to fill blocks within timeout") } diff --git a/core/header_test.go b/core/header_test.go index be4521d609..7b7eb3a7b7 100644 --- a/core/header_test.go +++ b/core/header_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/rand" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/header/headertest" @@ -40,7 +40,7 @@ func TestMakeExtendedHeaderForEmptyBlock(t *testing.T) { headerExt, err := header.MakeExtendedHeader(&b.Header, comm, val, eds) require.NoError(t, err) - assert.Equal(t, share.EmptyRoot(), headerExt.DAH) + assert.Equal(t, share.EmptyEDSRoots(), headerExt.DAH) } func TestMismatchedDataHash_ComputedRoot(t *testing.T) { diff --git a/core/listener.go b/core/listener.go index 5260067154..d403421175 100644 --- a/core/listener.go +++ b/core/listener.go @@ -12,13 +12,10 @@ import ( "go.opentelemetry.io/otel/attribute" libhead "github.com/celestiaorg/go-header" - "github.com/celestiaorg/nmt" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/pruner" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" + "github.com/celestiaorg/celestia-node/store" ) var ( @@ -39,8 +36,9 @@ type Listener struct { fetcher *BlockFetcher construct header.ConstructFn - store *eds.Store - availabilityWindow pruner.AvailabilityWindow + store *store.Store + availabilityWindow time.Duration + archival bool headerBroadcaster libhead.Broadcaster[*header.ExtendedHeader] hashBroadcaster shrexsub.BroadcastFn @@ -51,6 +49,7 @@ type Listener struct { listenerTimeout time.Duration cancel context.CancelFunc + closed chan struct{} } func NewListener( @@ -58,7 +57,7 @@ func NewListener( fetcher *BlockFetcher, hashBroadcaster shrexsub.BroadcastFn, construct header.ConstructFn, - store *eds.Store, + store *store.Store, blocktime time.Duration, opts ...Option, ) (*Listener, error) { @@ -85,6 +84,7 @@ func NewListener( construct: construct, store: store, availabilityWindow: p.availabilityWindow, + archival: p.archival, listenerTimeout: 5 * blocktime, metrics: metrics, chainID: p.chainID, @@ -99,6 +99,7 @@ func (cl *Listener) Start(context.Context) error { ctx, cancel := context.WithCancel(context.Background()) cl.cancel = cancel + cl.closed = make(chan struct{}) sub, err := cl.fetcher.SubscribeNewBlockEvent(ctx) if err != nil { @@ -116,13 +117,25 @@ func (cl *Listener) Stop(ctx context.Context) error { } cl.cancel() - cl.cancel = nil - return cl.metrics.Close() + select { + case <-cl.closed: + cl.cancel = nil + cl.closed = nil + case <-ctx.Done(): + return ctx.Err() + } + + err = cl.metrics.Close() + if err != nil { + log.Warnw("listener: closing metrics", "err", err) + } + return nil } // runSubscriber runs a subscriber to receive event data of new signed blocks. It will attempt to // resubscribe in case error happens during listening of subscription func (cl *Listener) runSubscriber(ctx context.Context, sub <-chan types.EventDataSignedBlock) { + defer close(cl.closed) for { err := cl.listen(ctx, sub) if ctx.Err() != nil { @@ -131,7 +144,7 @@ func (cl *Listener) runSubscriber(ctx context.Context, sub <-chan types.EventDat } if errors.Is(err, errInvalidSubscription) { // stop node if there is a critical issue with the block subscription - log.Fatalf("listener: %v", err) + log.Fatalf("listener: %v", err) //nolint:gocritic } log.Warnw("listener: subscriber error, resubscribing...", "err", err) @@ -214,11 +227,8 @@ func (cl *Listener) handleNewSignedBlock(ctx context.Context, b types.EventDataS span.SetAttributes( attribute.Int64("height", b.Header.Height), ) - // extend block data - adder := ipld.NewProofsAdder(int(b.Data.SquareSize)) - defer adder.Purge() - eds, err := extendBlock(b.Data, b.Header.Version.App, nmt.NodeVisitor(adder.VisitFn())) + eds, err := extendBlock(b.Data, b.Header.Version.App) if err != nil { return fmt.Errorf("extending block data: %w", err) } @@ -229,7 +239,7 @@ func (cl *Listener) handleNewSignedBlock(ctx context.Context, b types.EventDataS panic(fmt.Errorf("making extended header: %w", err)) } - err = storeEDS(ctx, eh, eds, adder, cl.store, cl.availabilityWindow) + err = storeEDS(ctx, eh, eds, cl.store, cl.availabilityWindow, cl.archival) if err != nil { return fmt.Errorf("storing EDS: %w", err) } diff --git a/core/listener_no_race_test.go b/core/listener_no_race_test.go index 51b1abe4ca..b7d26fba36 100644 --- a/core/listener_no_race_test.go +++ b/core/listener_no_race_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/store" ) // TestListenerWithNonEmptyBlocks ensures that non-empty blocks are actually @@ -28,11 +29,12 @@ func TestListenerWithNonEmptyBlocks(t *testing.T) { fetcher, cctx := createCoreFetcher(t, cfg) eds := createEdsPubSub(ctx, t) - store := createStore(t) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) // create Listener and start listening cl := createListener(ctx, t, fetcher, ps0, eds, store, testChainID) - err := cl.Start(ctx) + err = cl.Start(ctx) require.NoError(t, err) // listen for eds hashes broadcasted through eds-sub and ensure store has @@ -41,7 +43,7 @@ func TestListenerWithNonEmptyBlocks(t *testing.T) { require.NoError(t, err) t.Cleanup(sub.Cancel) - empty := share.EmptyRoot() + empty := share.EmptyEDSRoots() // TODO extract 16 for i := 0; i < 16; i++ { accounts := cfg.Genesis.Accounts() @@ -55,7 +57,11 @@ func TestListenerWithNonEmptyBlocks(t *testing.T) { continue } - has, err := store.Has(ctx, msg.DataHash) + has, err := store.HasByHash(ctx, msg.DataHash) + require.NoError(t, err) + require.True(t, has) + + has, err = store.HasByHeight(ctx, msg.Height) require.NoError(t, err) require.True(t, has) } diff --git a/core/listener_test.go b/core/listener_test.go index 60b6600468..99c6ea1a28 100644 --- a/core/listener_test.go +++ b/core/listener_test.go @@ -15,9 +15,8 @@ import ( "github.com/celestiaorg/celestia-node/header" nodep2p "github.com/celestiaorg/celestia-node/nodebuilder/p2p" - "github.com/celestiaorg/celestia-node/pruner" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" + "github.com/celestiaorg/celestia-node/store" ) const testChainID = "private" @@ -52,7 +51,9 @@ func TestListener(t *testing.T) { eds := createEdsPubSub(ctx, t) // create Listener and start listening - cl := createListener(ctx, t, fetcher, ps0, eds, createStore(t), testChainID) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) + cl := createListener(ctx, t, fetcher, ps0, eds, store, testChainID) err = cl.Start(ctx) require.NoError(t, err) @@ -84,7 +85,8 @@ func TestListenerWithWrongChainRPC(t *testing.T) { fetcher, _ := createCoreFetcher(t, cfg) eds := createEdsPubSub(ctx, t) - store := createStore(t) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) // create Listener and start listening cl := createListener(ctx, t, fetcher, ps0, eds, store, "wrong-chain-rpc") @@ -111,20 +113,21 @@ func TestListener_DoesNotStoreHistoric(t *testing.T) { fetcher, cctx := createCoreFetcher(t, cfg) eds := createEdsPubSub(ctx, t) - store := createStore(t) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) // create Listener and start listening - opt := WithAvailabilityWindow(pruner.AvailabilityWindow(time.Nanosecond)) + opt := WithAvailabilityWindow(time.Nanosecond) cl := createListener(ctx, t, fetcher, ps0, eds, store, testChainID, opt) dataRoots := generateNonEmptyBlocks(t, ctx, fetcher, cfg, cctx) - err := cl.Start(ctx) + err = cl.Start(ctx) require.NoError(t, err) // ensure none of the EDSes were stored for _, hash := range dataRoots { - has, err := store.Has(ctx, hash) + has, err := store.HasByHash(ctx, hash) require.NoError(t, err) assert.False(t, has) } @@ -171,7 +174,7 @@ func createListener( fetcher *BlockFetcher, ps *pubsub.PubSub, edsSub *shrexsub.PubSub, - store *eds.Store, + store *store.Store, chainID string, opts ...Option, ) *Listener { diff --git a/core/option.go b/core/option.go index e209d75aca..874246bd84 100644 --- a/core/option.go +++ b/core/option.go @@ -1,9 +1,9 @@ package core import ( + "time" + "github.com/celestiaorg/celestia-node/nodebuilder/p2p" - "github.com/celestiaorg/celestia-node/pruner" - "github.com/celestiaorg/celestia-node/pruner/archival" ) type Option func(*params) @@ -11,12 +11,14 @@ type Option func(*params) type params struct { metrics bool chainID string - availabilityWindow pruner.AvailabilityWindow + availabilityWindow time.Duration + archival bool } func defaultParams() params { return params{ - availabilityWindow: archival.Window, + availabilityWindow: time.Duration(0), + archival: false, } } @@ -34,8 +36,14 @@ func WithChainID(id p2p.Network) Option { } } -func WithAvailabilityWindow(window pruner.AvailabilityWindow) Option { +func WithAvailabilityWindow(window time.Duration) Option { return func(p *params) { p.availabilityWindow = window } } + +func WithArchivalMode() Option { + return func(p *params) { + p.archival = true + } +} diff --git a/core/testing.go b/core/testing.go index dafeaed756..6d2aa8cc36 100644 --- a/core/testing.go +++ b/core/testing.go @@ -10,8 +10,8 @@ import ( tmconfig "github.com/tendermint/tendermint/config" tmrand "github.com/tendermint/tendermint/libs/rand" - "github.com/celestiaorg/celestia-app/v2/test/util/genesis" - "github.com/celestiaorg/celestia-app/v2/test/util/testnode" + "github.com/celestiaorg/celestia-app/v3/test/util/genesis" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" ) const chainID = "private" diff --git a/das/coordinator.go b/das/coordinator.go index 852a40d24d..aff41bac8c 100644 --- a/das/coordinator.go +++ b/das/coordinator.go @@ -8,7 +8,7 @@ import ( libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) // samplingCoordinator runs and coordinates sampling workers and updates current sampling state diff --git a/das/coordinator_test.go b/das/coordinator_test.go index 18e707ba0d..a94a9a4e6f 100644 --- a/das/coordinator_test.go +++ b/das/coordinator_test.go @@ -14,7 +14,7 @@ import ( "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) func TestCoordinator(t *testing.T) { @@ -432,7 +432,7 @@ func (m *mockSampler) discover(ctx context.Context, newHeight uint64, emit liste emit(ctx, &header.ExtendedHeader{ Commit: &types.Commit{}, RawHeader: header.RawHeader{Height: int64(newHeight)}, - DAH: &share.Root{RowRoots: make([][]byte, 0)}, + DAH: &share.AxisRoots{RowRoots: make([][]byte, 0)}, }) } diff --git a/das/daser.go b/das/daser.go index b4ba434d24..d255d4e293 100644 --- a/das/daser.go +++ b/das/daser.go @@ -13,19 +13,13 @@ import ( libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/pruner" "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds/byzantine" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) var log = logging.Logger("das") -// errOutsideSamplingWindow is an error used to inform -// the caller of Sample that the given header is outside -// the sampling window. -var errOutsideSamplingWindow = fmt.Errorf("skipping header outside of sampling window") - // DASer continuously validates availability of data committed to headers. type DASer struct { params Parameters @@ -41,7 +35,7 @@ type DASer struct { cancel context.CancelFunc subscriberDone chan struct{} - running int32 + running atomic.Bool } type ( @@ -85,7 +79,7 @@ func NewDASer( // Start initiates subscription for new ExtendedHeaders and spawns a sampling routine. func (d *DASer) Start(ctx context.Context) error { - if !atomic.CompareAndSwapInt32(&d.running, 0, 1) { + if !d.running.CompareAndSwap(false, true) { return errors.New("da: DASer already started") } @@ -124,7 +118,7 @@ func (d *DASer) Start(ctx context.Context) error { // Stop stops sampling. func (d *DASer) Stop(ctx context.Context) error { - if !atomic.CompareAndSwapInt32(&d.running, 1, 0) { + if !d.running.CompareAndSwap(true, false) { return nil } @@ -160,14 +154,6 @@ func (d *DASer) Stop(ctx context.Context) error { } func (d *DASer) sample(ctx context.Context, h *header.ExtendedHeader) error { - // short-circuit if pruning is enabled and the header is outside the - // availability window - if !pruner.IsWithinAvailabilityWindow(h.Time(), d.params.samplingWindow) { - log.Debugw("skipping header outside sampling window", "height", h.Height(), - "time", h.Time()) - return errOutsideSamplingWindow - } - err := d.da.SharesAvailable(ctx, h) if err != nil { var byzantineErr *byzantine.ErrByzantine diff --git a/das/daser_test.go b/das/daser_test.go index 04517125fb..ac1d3b6190 100644 --- a/das/daser_test.go +++ b/das/daser_test.go @@ -2,37 +2,25 @@ package das import ( "context" - "strconv" + "fmt" "testing" "time" "github.com/golang/mock/gomock" - "github.com/ipfs/boxo/blockservice" "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" - pubsub "github.com/libp2p/go-libp2p-pubsub" - mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/go-fraud" - "github.com/celestiaorg/go-fraud/fraudserv" "github.com/celestiaorg/go-fraud/fraudtest" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/header/headertest" - headerfraud "github.com/celestiaorg/celestia-node/header/headertest/fraud" - "github.com/celestiaorg/celestia-node/pruner" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/availability/full" - "github.com/celestiaorg/celestia-node/share/availability/light" "github.com/celestiaorg/celestia-node/share/availability/mocks" - availability_test "github.com/celestiaorg/celestia-node/share/availability/test" - "github.com/celestiaorg/celestia-node/share/eds/byzantine" - "github.com/celestiaorg/celestia-node/share/getters" - "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/share/eds/edstest" ) var timeout = time.Second * 15 @@ -41,10 +29,11 @@ var timeout = time.Second * 15 // the DASer checkpoint is updated to network head. func TestDASerLifecycle(t *testing.T) { ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - bServ := ipld.NewMemBlockservice() - avail := light.TestAvailability(getters.NewIPLDGetter(bServ)) + ctrl := gomock.NewController(t) + avail := mocks.NewMockAvailability(ctrl) + avail.EXPECT().SharesAvailable(gomock.Any(), gomock.Any()).AnyTimes().Return(nil) // 15 headers from the past and 15 future headers - mockGet, sub, mockService := createDASerSubcomponents(t, bServ, 15, 15) + mockGet, sub, mockService := createDASerSubcomponents(t, 15, 15) ctx, cancel := context.WithTimeout(context.Background(), timeout) t.Cleanup(cancel) @@ -62,7 +51,7 @@ func TestDASerLifecycle(t *testing.T) { checkpoint, err := daser.store.load(ctx) require.NoError(t, err) // ensure checkpoint is stored at 30 - assert.EqualValues(t, 30, checkpoint.SampleFrom-1) + require.EqualValues(t, 30, checkpoint.SampleFrom-1) }() // wait for mock to indicate that catchup is done @@ -73,18 +62,16 @@ func TestDASerLifecycle(t *testing.T) { } // wait for DASer to indicate done - assert.NoError(t, daser.WaitCatchUp(ctx)) - - // give catch-up routine a second to finish up sampling last header - assert.NoError(t, daser.sampler.state.waitCatchUp(ctx)) + require.NoError(t, waitHeight(ctx, daser, 30)) } func TestDASer_Restart(t *testing.T) { ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - bServ := ipld.NewMemBlockservice() - avail := light.TestAvailability(getters.NewIPLDGetter(bServ)) + ctrl := gomock.NewController(t) + avail := mocks.NewMockAvailability(ctrl) + avail.EXPECT().SharesAvailable(gomock.Any(), gomock.Any()).AnyTimes().Return(nil) // 15 headers from the past and 15 future headers - mockGet, sub, mockService := createDASerSubcomponents(t, bServ, 15, 15) + mockGet, sub, mockService := createDASerSubcomponents(t, 15, 15) ctx, cancel := context.WithTimeout(context.Background(), timeout) t.Cleanup(cancel) @@ -103,16 +90,16 @@ func TestDASer_Restart(t *testing.T) { } // wait for DASer to indicate done - assert.NoError(t, daser.WaitCatchUp(ctx)) + require.NoError(t, waitHeight(ctx, daser, 30)) err = daser.Stop(ctx) require.NoError(t, err) // reset mockGet, generate 15 "past" headers, building off chain head which is 30 - mockGet.generateHeaders(t, bServ, 30, 45) + mockGet.generateHeaders(t, 30, 45) mockGet.doneCh = make(chan struct{}) // reset dummy subscriber - mockGet.fillSubWithHeaders(t, sub, bServ, 45, 60) + mockGet.fillSubWithHeaders(t, sub, 45, 60) // manually set mockGet head to trigger finished at 45 mockGet.head = int64(45) @@ -133,7 +120,7 @@ func TestDASer_Restart(t *testing.T) { case <-mockGet.doneCh: } - assert.NoError(t, daser.sampler.state.waitCatchUp(ctx)) + require.NoError(t, waitHeight(ctx, daser, 60)) err = daser.Stop(restartCtx) require.NoError(t, err) @@ -144,73 +131,76 @@ func TestDASer_Restart(t *testing.T) { assert.EqualValues(t, 60, checkpoint.SampleFrom-1) } -func TestDASer_stopsAfter_BEFP(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) - t.Cleanup(cancel) - - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - bServ := ipld.NewMemBlockservice() - // create mock network - net, err := mocknet.FullMeshLinked(1) - require.NoError(t, err) - // create pubsub for host - ps, err := pubsub.NewGossipSub(ctx, net.Hosts()[0], - pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign)) - require.NoError(t, err) - avail := full.TestAvailability(t, getters.NewIPLDGetter(bServ)) - // 15 headers from the past and 15 future headers - mockGet, sub, _ := createDASerSubcomponents(t, bServ, 15, 15) - - // create fraud service and break one header - getter := func(ctx context.Context, height uint64) (*header.ExtendedHeader, error) { - return mockGet.GetByHeight(ctx, height) - } - headGetter := func(ctx context.Context) (*header.ExtendedHeader, error) { - return mockGet.Head(ctx) - } - unmarshaler := fraud.MultiUnmarshaler[*header.ExtendedHeader]{ - Unmarshalers: map[fraud.ProofType]func([]byte) (fraud.Proof[*header.ExtendedHeader], error){ - byzantine.BadEncoding: func(data []byte) (fraud.Proof[*header.ExtendedHeader], error) { - befp := &byzantine.BadEncodingProof{} - return befp, befp.UnmarshalBinary(data) - }, - }, - } - - fserv := fraudserv.NewProofService[*header.ExtendedHeader](ps, - net.Hosts()[0], - getter, - headGetter, - unmarshaler, - ds, - false, - "private", - ) - require.NoError(t, fserv.Start(ctx)) - mockGet.headers[1] = headerfraud.CreateFraudExtHeader(t, mockGet.headers[1], bServ) - newCtx := context.Background() - - // create and start DASer - daser, err := NewDASer(avail, sub, mockGet, ds, fserv, newBroadcastMock(1)) - require.NoError(t, err) - - resultCh := make(chan error) - go fraud.OnProof[*header.ExtendedHeader](newCtx, fserv, byzantine.BadEncoding, - func(fraud.Proof[*header.ExtendedHeader]) { - resultCh <- daser.Stop(newCtx) - }) - - require.NoError(t, daser.Start(newCtx)) - // wait for fraud proof will be handled - select { - case <-ctx.Done(): - t.Fatal(ctx.Err()) - case res := <-resultCh: - require.NoError(t, res) - } - // wait for manager to finish catchup - require.True(t, daser.running == 0) -} +// TODO(@walldiss): BEFP test will not work until BEFP-shwap integration +// func TestDASer_stopsAfter_BEFP(t *testing.T) { +// ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) +// t.Cleanup(cancel) +// +// ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) +// // create mock network +// net, err := mocknet.FullMeshLinked(1) +// require.NoError(t, err) +// // create pubsub for host +// ps, err := pubsub.NewGossipSub(ctx, net.Hosts()[0], +// pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign)) +// require.NoError(t, err) +// +// ctrl := gomock.NewController(t) +// avail := mocks.NewMockAvailability(ctrl) +// avail.EXPECT().SharesAvailable(gomock.Any(), gomock.Any()).AnyTimes().Return(nil) +// // 15 headers from the past and 15 future headers +// mockGet, sub, _ := createDASerSubcomponents(t, 15, 15) +// +// // create fraud service and break one header +// getter := func(ctx context.Context, height uint64) (*header.ExtendedHeader, error) { +// return mockGet.GetByHeight(ctx, height) +// } +// headGetter := func(ctx context.Context) (*header.ExtendedHeader, error) { +// return mockGet.Head(ctx) +// } +// unmarshaler := fraud.MultiUnmarshaler[*header.ExtendedHeader]{ +// Unmarshalers: map[fraud.ProofType]func([]byte) (fraud.Proof[*header.ExtendedHeader], error){ +// byzantine.BadEncoding: func(data []byte) (fraud.Proof[*header.ExtendedHeader], error) { +// befp := &byzantine.BadEncodingProof{} +// return befp, befp.UnmarshalBinary(data) +// }, +// }, +// } +// +// fserv := fraudserv.NewProofService[*header.ExtendedHeader](ps, +// net.Hosts()[0], +// getter, +// headGetter, +// unmarshaler, +// ds, +// false, +// "private", +// ) +// require.NoError(t, fserv.Start(ctx)) +// mockGet.headers[1] = headerfraud.CreateFraudExtHeader(t, mockGet.headers[1]) +// newCtx := context.Background() +// +// // create and start DASer +// daser, err := NewDASer(avail, sub, mockGet, ds, fserv, newBroadcastMock(1)) +// require.NoError(t, err) +// +// resultCh := make(chan error) +// go fraud.OnProof[*header.ExtendedHeader](newCtx, fserv, byzantine.BadEncoding, +// func(fraud.Proof[*header.ExtendedHeader]) { +// resultCh <- daser.Stop(newCtx) +// }) +// +// require.NoError(t, daser.Start(newCtx)) +// // wait for fraud proof will be handled +// select { +// case <-ctx.Done(): +// t.Fatal(ctx.Err()) +// case res := <-resultCh: +// require.NoError(t, res) +// } +// // wait for manager to finish catchup +// require.False(t, daser.running.Load()) +//} func TestDASerSampleTimeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) @@ -219,17 +209,18 @@ func TestDASerSampleTimeout(t *testing.T) { getter := getterStub{} avail := mocks.NewMockAvailability(gomock.NewController(t)) doneCh := make(chan struct{}) - avail.EXPECT().SharesAvailable(gomock.Any(), gomock.Any()).DoAndReturn( - func(sampleCtx context.Context, h *header.ExtendedHeader) error { - select { - case <-sampleCtx.Done(): - close(doneCh) - return nil - case <-ctx.Done(): - t.Fatal("call context didn't timeout in time") - return ctx.Err() - } - }) + avail.EXPECT().SharesAvailable(gomock.Any(), gomock.Any()).AnyTimes(). + DoAndReturn( + func(sampleCtx context.Context, h *header.ExtendedHeader) error { + select { + case <-sampleCtx.Done(): + close(doneCh) + return nil + case <-ctx.Done(): + t.Fatal("call context didn't timeout in time") + return ctx.Err() + } + }) ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) sub := new(headertest.Subscriber) @@ -250,63 +241,22 @@ func TestDASerSampleTimeout(t *testing.T) { } } -// TestDASer_SamplingWindow tests the sampling window determination -// for headers. -func TestDASer_SamplingWindow(t *testing.T) { - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - sub := new(headertest.Subscriber) - fserv := &fraudtest.DummyService[*header.ExtendedHeader]{} - getter := getterStub{} - avail := mocks.NewMockAvailability(gomock.NewController(t)) - - // create and start DASer - daser, err := NewDASer(avail, sub, getter, ds, fserv, newBroadcastMock(1), - WithSamplingWindow(pruner.AvailabilityWindow(time.Second))) - require.NoError(t, err) - - tests := []struct { - timestamp time.Time - withinWindow bool - }{ - {timestamp: time.Now().Add(-(time.Second * 5)), withinWindow: false}, - {timestamp: time.Now().Add(-(time.Millisecond * 800)), withinWindow: true}, - {timestamp: time.Now().Add(-(time.Hour)), withinWindow: false}, - {timestamp: time.Now().Add(-(time.Hour * 24 * 30)), withinWindow: false}, - {timestamp: time.Now(), withinWindow: true}, - } - - for i, tt := range tests { - t.Run(strconv.Itoa(i), func(t *testing.T) { - eh := headertest.RandExtendedHeader(t) - eh.RawHeader.Time = tt.timestamp - - assert.Equal( - t, - tt.withinWindow, - pruner.IsWithinAvailabilityWindow(eh.Time(), daser.params.samplingWindow), - ) - }) - } -} - // createDASerSubcomponents takes numGetter (number of headers // to store in mockGetter) and numSub (number of headers to store // in the mock header.Subscriber), returning a newly instantiated // mockGetter, share.Availability, and mock header.Subscriber. func createDASerSubcomponents( t *testing.T, - bServ blockservice.BlockService, numGetter, numSub int, ) (*mockGetter, *headertest.Subscriber, *fraudtest.DummyService[*header.ExtendedHeader]) { - mockGet, sub := createMockGetterAndSub(t, bServ, numGetter, numSub) + mockGet, sub := createMockGetterAndSub(t, numGetter, numSub) fraud := &fraudtest.DummyService[*header.ExtendedHeader]{} return mockGet, sub, fraud } func createMockGetterAndSub( t *testing.T, - bServ blockservice.BlockService, numGetter, numSub int, ) (*mockGetter, *headertest.Subscriber) { @@ -316,10 +266,10 @@ func createMockGetterAndSub( brokenHeightCh: make(chan struct{}), } - mockGet.generateHeaders(t, bServ, 0, numGetter) + mockGet.generateHeaders(t, 0, numGetter) sub := new(headertest.Subscriber) - mockGet.fillSubWithHeaders(t, sub, bServ, numGetter, numGetter+numSub) + mockGet.fillSubWithHeaders(t, sub, numGetter, numGetter+numSub) return mockGet, sub } @@ -327,7 +277,6 @@ func createMockGetterAndSub( func (m *mockGetter) fillSubWithHeaders( t *testing.T, sub *headertest.Subscriber, - bServ blockservice.BlockService, startHeight, endHeight int, ) { @@ -335,9 +284,8 @@ func (m *mockGetter) fillSubWithHeaders( index := 0 for i := startHeight; i < endHeight; i++ { - dah := availability_test.RandFillBS(t, 16, bServ) - - randHeader := headertest.RandExtendedHeaderWithRoot(t, dah) + roots := edstest.RandomAxisRoots(t, 16) + randHeader := headertest.RandExtendedHeaderWithRoot(t, roots) randHeader.RawHeader.Height = int64(i + 1) sub.Headers[index] = randHeader @@ -359,11 +307,11 @@ type mockGetter struct { headers map[int64]*header.ExtendedHeader } -func (m *mockGetter) generateHeaders(t *testing.T, bServ blockservice.BlockService, startHeight, endHeight int) { +func (m *mockGetter) generateHeaders(t *testing.T, startHeight, endHeight int) { for i := startHeight; i < endHeight; i++ { - dah := availability_test.RandFillBS(t, 16, bServ) + roots := edstest.RandomAxisRoots(t, 16) - randHeader := headertest.RandExtendedHeaderWithRoot(t, dah) + randHeader := headertest.RandExtendedHeaderWithRoot(t, roots) randHeader.RawHeader.Height = int64(i + 1) m.headers[int64(i+1)] = randHeader @@ -397,7 +345,10 @@ func (m *mockGetter) GetByHeight(_ context.Context, height uint64) (*header.Exte } }() - return m.headers[int64(height)], nil + if h, ok := m.headers[int64(height)]; ok { + return h, nil + } + return nil, fmt.Errorf("header not found") } type benchGetterStub struct { @@ -407,7 +358,7 @@ type benchGetterStub struct { func newBenchGetter() benchGetterStub { return benchGetterStub{header: &header.ExtendedHeader{ - DAH: &share.Root{RowRoots: make([][]byte, 0)}, + DAH: &share.AxisRoots{RowRoots: make([][]byte, 0)}, }} } @@ -428,7 +379,7 @@ func (m getterStub) GetByHeight(_ context.Context, height uint64) (*header.Exten return &header.ExtendedHeader{ Commit: &types.Commit{}, RawHeader: header.RawHeader{Height: int64(height)}, - DAH: &share.Root{RowRoots: make([][]byte, 0)}, + DAH: &share.AxisRoots{RowRoots: make([][]byte, 0)}, }, nil } @@ -443,3 +394,22 @@ func (m getterStub) GetRangeByHeight( func (m getterStub) Get(context.Context, libhead.Hash) (*header.ExtendedHeader, error) { panic("implement me") } + +// waitHeight waits for the DASer to catch up to the given height. It will return an error if the +// DASer fails to catch up to the given height within the timeout. +func waitHeight(ctx context.Context, daser *DASer, height uint64) error { + for { + err := daser.WaitCatchUp(ctx) + if err != nil { + return err + } + stats, err := daser.SamplingStats(ctx) + if err != nil { + return err + } + if stats.SampledChainHead == height { + return nil + } + time.Sleep(time.Millisecond * 100) + } +} diff --git a/das/metrics.go b/das/metrics.go index 82cf0afec8..759fa2e2d5 100644 --- a/das/metrics.go +++ b/das/metrics.go @@ -28,7 +28,7 @@ type metrics struct { getHeaderTime metric.Float64Histogram newHead metric.Int64Counter - lastSampledTS uint64 + lastSampledTS atomic.Uint64 clientReg metric.Registration } @@ -113,7 +113,7 @@ func (d *DASer) InitMetrics() error { observer.ObserveInt64(networkHead, int64(stats.NetworkHead)) observer.ObserveInt64(sampledChainHead, int64(stats.SampledChainHead)) - if ts := atomic.LoadUint64(&d.sampler.metrics.lastSampledTS); ts != 0 { + if ts := d.sampler.metrics.lastSampledTS.Load(); ts != 0 { observer.ObserveInt64(lastSampledTS, int64(ts)) } @@ -171,7 +171,7 @@ func (m *metrics) observeSample( attribute.String(jobTypeLabel, string(jobType)), )) - atomic.StoreUint64(&m.lastSampledTS, uint64(time.Now().UTC().Unix())) + m.lastSampledTS.Store(uint64(time.Now().UTC().Unix())) } // observeGetHeader records the time it took to get a header from the header store. diff --git a/das/options.go b/das/options.go index d70d8fdd04..cd7444ac16 100644 --- a/das/options.go +++ b/das/options.go @@ -4,8 +4,6 @@ import ( "errors" "fmt" "time" - - "github.com/celestiaorg/celestia-node/pruner" ) // ErrInvalidOption is an error that is returned by Parameters.Validate @@ -43,11 +41,6 @@ type Parameters struct { // divided between parallel workers. SampleTimeout should be adjusted proportionally to // ConcurrencyLimit. SampleTimeout time.Duration - - // samplingWindow determines the time window that headers should fall into - // in order to be sampled. If set to 0, the sampling window will include - // all headers. - samplingWindow pruner.AvailabilityWindow } // DefaultParameters returns the default configuration values for the daser parameters @@ -163,11 +156,3 @@ func WithSampleTimeout(sampleTimeout time.Duration) Option { d.params.SampleTimeout = sampleTimeout } } - -// WithSamplingWindow is a functional option to configure the DASer's -// `samplingWindow` parameter. -func WithSamplingWindow(samplingWindow pruner.AvailabilityWindow) Option { - return func(d *DASer) { - d.params.samplingWindow = samplingWindow - } -} diff --git a/das/worker.go b/das/worker.go index 4cccb77796..b9dff58445 100644 --- a/das/worker.go +++ b/das/worker.go @@ -10,7 +10,8 @@ import ( libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/availability" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) type jobType string @@ -83,7 +84,7 @@ func (w *worker) run(ctx context.Context, timeout time.Duration, resultCh chan<- // sampling worker will resume upon restart return } - if errors.Is(err, errOutsideSamplingWindow) { + if errors.Is(err, availability.ErrOutsideSamplingWindow) { skipped++ err = nil } @@ -119,7 +120,7 @@ func (w *worker) sample(ctx context.Context, timeout time.Duration, height uint6 defer cancel() err = w.sampleFn(ctx, h) - if errors.Is(err, errOutsideSamplingWindow) { + if errors.Is(err, availability.ErrOutsideSamplingWindow) { // if header is outside sampling window, do not log // or record it. return err @@ -127,18 +128,20 @@ func (w *worker) sample(ctx context.Context, timeout time.Duration, height uint6 w.metrics.observeSample(ctx, h, time.Since(start), w.state.jobType, err) if err != nil { - if !errors.Is(err, context.Canceled) { - log.Debugw( - "failed to sample header", - "type", w.state.jobType, - "height", h.Height(), - "hash", h.Hash(), - "square width", len(h.DAH.RowRoots), - "data root", h.DAH.String(), - "err", err, - "finished (s)", time.Since(start), - ) + if errors.Is(err, context.Canceled) { + return err } + + log.Errorw( + "failed to sample header", + "type", w.state.jobType, + "height", h.Height(), + "hash", h.Hash(), + "square width", len(h.DAH.RowRoots), + "data root", h.DAH.String(), + "err", err, + "finished (s)", time.Since(start), + ) return err } @@ -163,7 +166,7 @@ func (w *worker) sample(ctx context.Context, timeout time.Duration, height uint6 "type", w.state.jobType, "height", h.Height(), "hash", h.Hash(), - "square width", len(h.DAH.RowRoots), + "EDS square width", len(h.DAH.RowRoots), "data root", h.DAH.String(), "finished (s)", time.Since(start), ) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 6f064be830..95784ebf9c 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -4,13 +4,14 @@ set -e if [ "$1" = 'celestia' ]; then echo "Initializing Celestia Node with command:" - + COMMAND="celestia "${NODE_TYPE}" init --p2p.network "${P2P_NETWORK}" --rpc.addr="0.0.0.0" --gateway.addr="0.0.0.0"" if [[ -n "$NODE_STORE" ]]; then - echo "celestia "${NODE_TYPE}" init --p2p.network "${P2P_NETWORK}" --node.store "${NODE_STORE}"" - celestia "${NODE_TYPE}" init --p2p.network "${P2P_NETWORK}" --node.store "${NODE_STORE}" + COMMAND=${COMMAND}" --node.store "${NODE_STORE}"" + echo $COMMAND + $COMMAND else - echo "celestia "${NODE_TYPE}" init --p2p.network "${P2P_NETWORK}"" - celestia "${NODE_TYPE}" init --p2p.network "${P2P_NETWORK}" + echo $COMMAND + $COMMAND fi echo "" diff --git a/go.mod b/go.mod index 27a6253524..d7f68afc10 100644 --- a/go.mod +++ b/go.mod @@ -1,25 +1,24 @@ module github.com/celestiaorg/celestia-node -go 1.23.0 +go 1.23.2 require ( - cosmossdk.io/math v1.3.0 - github.com/BurntSushi/toml v1.4.0 + cosmossdk.io/math v1.4.0 + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b github.com/benbjohnson/clock v1.3.5 - github.com/celestiaorg/celestia-app/v2 v2.1.2 + github.com/celestiaorg/celestia-app/v3 v3.2.0 github.com/celestiaorg/go-fraud v0.2.1 - github.com/celestiaorg/go-header v0.6.2 + github.com/celestiaorg/go-header v0.6.3 github.com/celestiaorg/go-libp2p-messenger v0.2.0 - github.com/celestiaorg/go-square v1.1.0 github.com/celestiaorg/go-square/merkle v0.0.0-20240117232118-fd78256df076 - github.com/celestiaorg/nmt v0.22.1 - github.com/celestiaorg/rsmt2d v0.13.1 + github.com/celestiaorg/go-square/v2 v2.1.0 + github.com/celestiaorg/nmt v0.22.2 + github.com/celestiaorg/rsmt2d v0.14.0 github.com/cosmos/cosmos-sdk v0.46.16 github.com/cristalhq/jwt/v5 v5.4.0 - github.com/dgraph-io/badger/v4 v4.2.1-0.20240106094458-1c417aa3799c + github.com/dgraph-io/badger/v4 v4.3.0 github.com/etclabscore/go-openrpc-reflect v0.0.37 - github.com/filecoin-project/dagstore v0.5.6 github.com/filecoin-project/go-jsonrpc v0.6.0 github.com/gammazero/workerpool v1.1.3 github.com/gofrs/flock v0.12.1 @@ -31,56 +30,57 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/imdario/mergo v0.3.16 - github.com/ipfs/boxo v0.22.0 + github.com/ipfs/boxo v0.24.0 github.com/ipfs/go-block-format v0.2.0 github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-ds-badger4 v0.1.5 - github.com/ipfs/go-ipld-cbor v0.1.0 + github.com/ipfs/go-ipfs-delay v0.0.1 github.com/ipfs/go-ipld-format v0.6.0 github.com/ipfs/go-log/v2 v2.5.1 + github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-prometheus v0.0.2 - github.com/ipld/go-car v0.6.2 - github.com/libp2p/go-libp2p v0.36.2 - github.com/libp2p/go-libp2p-kad-dht v0.26.1 - github.com/libp2p/go-libp2p-pubsub v0.11.0 + github.com/klauspost/reedsolomon v1.12.1 + github.com/libp2p/go-libp2p v0.37.2 + github.com/libp2p/go-libp2p-kad-dht v0.27.0 + github.com/libp2p/go-libp2p-pubsub v0.12.0 github.com/libp2p/go-libp2p-record v0.2.0 github.com/libp2p/go-libp2p-routing-helpers v0.7.4 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.1.0 github.com/multiformats/go-multiaddr v0.13.0 - github.com/multiformats/go-multiaddr-dns v0.3.1 + github.com/multiformats/go-multiaddr-dns v0.4.1 github.com/multiformats/go-multihash v0.2.3 github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 - github.com/prometheus/client_golang v1.19.1 - github.com/rollkit/go-da v0.5.0 + github.com/prometheus/client_golang v1.20.5 + github.com/rollkit/go-da v0.8.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 github.com/tendermint/tendermint v0.34.29 go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0 - go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 - go.opentelemetry.io/otel/metric v1.27.0 - go.opentelemetry.io/otel/sdk v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 + go.opentelemetry.io/otel/metric v1.31.0 + go.opentelemetry.io/otel/sdk v1.31.0 go.opentelemetry.io/otel/sdk/metric v1.27.0 - go.opentelemetry.io/otel/trace v1.27.0 + go.opentelemetry.io/otel/trace v1.31.0 go.opentelemetry.io/proto/otlp v1.3.1 - go.uber.org/fx v1.22.1 + go.uber.org/fx v1.23.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.25.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/sync v0.7.0 - golang.org/x/text v0.16.0 - google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.2 + golang.org/x/crypto v0.29.0 + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/sync v0.9.0 + golang.org/x/text v0.20.0 + google.golang.org/grpc v1.68.0 + google.golang.org/protobuf v1.35.1 ) require ( cloud.google.com/go v0.112.1 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect cosmossdk.io/errors v1.0.1 // indirect @@ -88,6 +88,7 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect + github.com/DataDog/zstd v1.5.0 // indirect github.com/Jorropo/jsync v1.0.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/StackExchange/wmi v1.2.1 // indirect @@ -97,17 +98,24 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/bits-and-blooms/bitset v1.10.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/celestiaorg/blobstream-contracts/v3 v3.1.0 // indirect + github.com/celestiaorg/go-square v1.1.1 // indirect github.com/celestiaorg/merkletree v0.0.0-20230308153949-c33506a7aa26 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.2 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect - github.com/cometbft/cometbft-db v0.7.0 // indirect + github.com/cometbft/cometbft-db v1.0.1 // indirect github.com/confio/ics23/go v0.9.1 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect @@ -116,14 +124,14 @@ require ( github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/gogoproto v1.5.0 // indirect + github.com/cosmos/gogoproto v1.7.0 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/cosmos/iavl v0.19.6 // indirect github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6 v6.1.2 // indirect github.com/cosmos/ibc-go/v6 v6.3.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.2 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect - github.com/creachadair/taskgroup v0.3.2 // indirect github.com/cskr/pubsub v1.0.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -132,20 +140,23 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect - github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/elastic/gosigar v0.14.3 // indirect github.com/etclabscore/go-jsonschema-walk v0.0.6 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect - github.com/ethereum/go-ethereum v1.14.5 // indirect + github.com/ethereum/go-ethereum v1.14.11 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gammazero/deque v0.2.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect @@ -155,21 +166,20 @@ require ( github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/spec v0.19.11 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/gateway v1.1.0 // indirect - github.com/golang/glog v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/orderedcode v0.0.1 // indirect - github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect + github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect @@ -179,7 +189,7 @@ require ( github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -194,26 +204,17 @@ require ( github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect - github.com/holiman/uint256 v1.2.4 // indirect + github.com/holiman/uint256 v1.3.1 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect - github.com/ipfs/go-blockservice v0.5.2 // indirect - github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect - github.com/ipfs/go-ipfs-delay v0.0.1 // indirect - github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect - github.com/ipfs/go-ipfs-exchange-interface v0.2.1 // indirect github.com/ipfs/go-ipfs-pq v0.0.3 // indirect github.com/ipfs/go-ipfs-util v0.0.3 // indirect github.com/ipfs/go-ipld-legacy v0.2.1 // indirect github.com/ipfs/go-log v1.0.5 // indirect - github.com/ipfs/go-merkledag v0.11.0 // indirect - github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect - github.com/ipfs/go-verifcid v0.0.3 // indirect - github.com/ipld/go-car/v2 v2.13.1 // indirect github.com/ipld/go-codec-dagpb v1.6.0 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect @@ -222,32 +223,34 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect - github.com/klauspost/reedsolomon v1.12.1 // indirect github.com/koron/go-ssdp v0.0.4 // indirect - github.com/lib/pq v1.10.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect - github.com/libp2p/go-flow-metrics v0.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect + github.com/libp2p/go-libp2p-kbucket v0.6.4 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect + github.com/linxGnu/grocksdb v1.9.3 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/miekg/dns v1.1.61 // indirect + github.com/miekg/dns v1.1.62 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect - github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/highwayhash v1.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -258,45 +261,45 @@ require ( github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect - github.com/multiformats/go-multistream v0.5.0 // indirect + github.com/multiformats/go-multistream v0.6.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/ginkgo/v2 v2.19.1 // indirect + github.com/onsi/ginkgo/v2 v2.20.2 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect - github.com/pion/datachannel v1.5.8 // indirect + github.com/pion/datachannel v1.5.9 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect - github.com/pion/ice/v2 v2.3.34 // indirect - github.com/pion/interceptor v0.1.29 // indirect + github.com/pion/ice/v2 v2.3.36 // indirect + github.com/pion/interceptor v0.1.37 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.12 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.14 // indirect - github.com/pion/rtp v1.8.8 // indirect - github.com/pion/sctp v1.8.20 // indirect + github.com/pion/rtp v1.8.9 // indirect + github.com/pion/sctp v1.8.33 // indirect github.com/pion/sdp/v3 v3.0.9 // indirect github.com/pion/srtp/v2 v2.0.20 // indirect github.com/pion/stun v0.6.1 // indirect github.com/pion/transport/v2 v2.2.10 // indirect github.com/pion/turn/v2 v2.1.6 // indirect - github.com/pion/webrtc/v3 v3.3.0 // indirect + github.com/pion/webrtc/v3 v3.3.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.60.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/quic-go v0.45.2 // indirect - github.com/quic-go/webtransport-go v0.8.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.48.2 // indirect + github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect github.com/rakyll/statik v0.1.7 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/regen-network/cosmos-proto v0.3.1 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/cors v1.8.3 // indirect github.com/rs/zerolog v1.33.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect @@ -307,57 +310,55 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.15.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/supranational/blst v0.3.11 // indirect + github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tendermint/tm-db v0.6.7 // indirect github.com/tidwall/btree v1.5.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/ulikunitz/xz v0.5.10 // indirect - github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect - github.com/whyrusleeping/cbor-gen v0.1.2 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect - github.com/wlynxg/anet v0.0.3 // indirect + github.com/wlynxg/anet v0.0.5 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect - go.etcd.io/bbolt v1.3.6 // indirect + go.etcd.io/bbolt v1.3.11 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 // indirect - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/dig v1.17.1 // indirect - go.uber.org/mock v0.4.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 // indirect + go.uber.org/dig v1.18.0 // indirect + go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.27.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/term v0.22.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/term v0.26.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.23.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + golang.org/x/tools v0.26.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/api v0.169.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect - nhooyr.io/websocket v1.8.7 // indirect + nhooyr.io/websocket v1.8.17 // indirect rsc.io/tmplfunc v0.0.3 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) replace ( - github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.24.1-sdk-v0.46.16 + github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.25.1-sdk-v0.46.16 github.com/filecoin-project/dagstore => github.com/celestiaorg/dagstore v0.0.0-20230824094345-537c012aa403 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 // broken goleveldb needs to be replaced for the cosmos-sdk and celestia-app github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.40.0-tm-v0.34.29 + github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.44.1-tm-v0.34.35 ) + +replace github.com/ipfs/boxo => github.com/celestiaorg/boxo v0.0.0-20241118122411-70a650316c3b diff --git a/go.sum b/go.sum index b2a56315e9..2f11f635a0 100644 --- a/go.sum +++ b/go.sum @@ -73,8 +73,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -190,11 +190,10 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= -cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= -cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +cosmossdk.io/math v1.4.0 h1:XbgExXFnXmF/CccPPEto40gOO7FpWu9yWNAZPN3nkNQ= +cosmossdk.io/math v1.4.0/go.mod h1:O5PkD4apz2jZs4zqFdTr16e1dcaQCc5z6lkEnrrppuk= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= @@ -207,8 +206,6 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= -github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= @@ -217,8 +214,8 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= @@ -229,10 +226,10 @@ github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwS github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -246,7 +243,6 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= @@ -268,7 +264,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= @@ -309,16 +304,13 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1U github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= -github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs= @@ -326,9 +318,8 @@ github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= @@ -346,38 +337,40 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/bufbuild/protocompile v0.5.1 h1:mixz5lJX4Hiz4FpqFREJHIXLfaLBntfaJv1h+/jS+Qg= -github.com/bufbuild/protocompile v0.5.1/go.mod h1:G5iLmavmF4NsYtpZFvE3B/zFch2GIY8+wjsYLR/lc40= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/celestiaorg/blobstream-contracts/v3 v3.1.0 h1:h1Y4V3EMQ2mFmNtWt2sIhZIuyASInj1a9ExI8xOsTOw= github.com/celestiaorg/blobstream-contracts/v3 v3.1.0/go.mod h1:x4DKyfKOSv1ZJM9NwV+Pw01kH2CD7N5zTFclXIVJ6GQ= -github.com/celestiaorg/celestia-app/v2 v2.1.2 h1:/3NhEPkVHahKrJ3blehDPjy7AzWq8z68afgvEmor/tk= -github.com/celestiaorg/celestia-app/v2 v2.1.2/go.mod h1:qraGN1WNAtIFwGWB0NWnZ3tGPL5joPlbLStSZ4k6niQ= -github.com/celestiaorg/celestia-core v1.40.0-tm-v0.34.29 h1:J79TAjizxwIvm7/k+WI3PPH1aFj4AjOSjajoq5UzAwI= -github.com/celestiaorg/celestia-core v1.40.0-tm-v0.34.29/go.mod h1:5jJ5magtH7gQOwSYfS/m5fliIS7irKunLV7kLNaD8o0= -github.com/celestiaorg/cosmos-sdk v1.24.1-sdk-v0.46.16 h1:SeQ7Y/CyOcUMKo7mQiexaj/pZ/xIgyuZFIwYZwpSkWE= -github.com/celestiaorg/cosmos-sdk v1.24.1-sdk-v0.46.16/go.mod h1:Bpl1LSWiDpQumgOhhMTZBMopqa0j7fRasIhvTZB44P0= -github.com/celestiaorg/dagstore v0.0.0-20230824094345-537c012aa403 h1:Lj73O3S+KJx5/hgZ+IeOLEIoLsAveJN/7/ZtQQtPSVw= -github.com/celestiaorg/dagstore v0.0.0-20230824094345-537c012aa403/go.mod h1:cCGM1UoMvyTk8k62mkc+ReVu8iHBCtSBAAL4wYU7KEI= +github.com/celestiaorg/boxo v0.0.0-20241118122411-70a650316c3b h1:M9X7s1WJ/7Ju84ZUbO/6/8XlODkFsj/ln85AE0F6pj8= +github.com/celestiaorg/boxo v0.0.0-20241118122411-70a650316c3b/go.mod h1:OpUrJtGmZZktUqJvPOtmP8wSfEFcdF/55d3PNCcYLwc= +github.com/celestiaorg/celestia-app/v3 v3.2.0 h1:s6Yag6hLvUQGwS4TXIwtbgmFT1uDvvjgM8OGPUK5de4= +github.com/celestiaorg/celestia-app/v3 v3.2.0/go.mod h1:heMutO+/UMfH0RXCqosZBrccLWz9krwgaoQmcEEUb4A= +github.com/celestiaorg/celestia-core v1.44.1-tm-v0.34.35 h1:ZPbLW+E8galFjKb+Wl4ZGKS8IUNgzWTrRSBfs1izPcQ= +github.com/celestiaorg/celestia-core v1.44.1-tm-v0.34.35/go.mod h1:bFr0lAGwaJ0mOHSBmib5/ca5pbBf1yKWGPs93Td0HPw= +github.com/celestiaorg/cosmos-sdk v1.25.1-sdk-v0.46.16 h1:W0c1Ib24jGZ3UDnzupoD8PmscnOU39qhVV5qJ69yBvQ= +github.com/celestiaorg/cosmos-sdk v1.25.1-sdk-v0.46.16/go.mod h1:07Z8HJqS8Rw4XlZ+ok3D3NM/X/in8mvcGLvl0Zb5wrA= github.com/celestiaorg/go-fraud v0.2.1 h1:oYhxI0gM/EpGRgbVQdRI/LSlqyT65g/WhQGSVGfx09w= github.com/celestiaorg/go-fraud v0.2.1/go.mod h1:lNY1i4K6kUeeE60Z2VK8WXd+qXb8KRzfBhvwPkK6aUc= -github.com/celestiaorg/go-header v0.6.2 h1:qgWyJQg+/x6k4QAfN1rPt2HXHZjQOmCqD0ct4dFBIZY= -github.com/celestiaorg/go-header v0.6.2/go.mod h1:Az4S4NxMOJ1eAzOaF8u5AZt5UzsSzg92uqpdXS3yOZE= +github.com/celestiaorg/go-header v0.6.3 h1:VI+fsNxFLeUS7cNn0LgHP6Db66uslnKp/fgMg5nxqHg= +github.com/celestiaorg/go-header v0.6.3/go.mod h1:Az4S4NxMOJ1eAzOaF8u5AZt5UzsSzg92uqpdXS3yOZE= github.com/celestiaorg/go-libp2p-messenger v0.2.0 h1:/0MuPDcFamQMbw9xTZ73yImqgTO3jHV7wKHvWD/Irao= github.com/celestiaorg/go-libp2p-messenger v0.2.0/go.mod h1:s9PIhMi7ApOauIsfBcQwbr7m+HBzmVfDIS+QLdgzDSo= -github.com/celestiaorg/go-square v1.1.0 h1:K4tBL5PCJwDtpBfyDxxZ3N962aC9VYb5/bw3LjagEtY= -github.com/celestiaorg/go-square v1.1.0/go.mod h1:1EXMErhDrWJM8B8V9hN7dqJ2kUTClfwdqMOmF9yQUa0= +github.com/celestiaorg/go-square v1.1.1 h1:Cy3p8WVspVcyOqHM8BWFuuYPwMitO1pYGe+ImILFZRA= +github.com/celestiaorg/go-square v1.1.1/go.mod h1:1EXMErhDrWJM8B8V9hN7dqJ2kUTClfwdqMOmF9yQUa0= github.com/celestiaorg/go-square/merkle v0.0.0-20240117232118-fd78256df076 h1:PYInrsYzrDIsZW9Yb86OTi2aEKuPcpgJt6Mc0Jlc/yg= github.com/celestiaorg/go-square/merkle v0.0.0-20240117232118-fd78256df076/go.mod h1:hlidgivKyvv7m4Yl2Fdf2mSTmazZYxX8+bnr5IQrI98= +github.com/celestiaorg/go-square/v2 v2.1.0 h1:ECIvYEeHIWiIJGDCJxQNtzqm5DmnBly7XGhSpLsl+Lw= +github.com/celestiaorg/go-square/v2 v2.1.0/go.mod h1:n3ztrh8CBjWOD6iWYMo3pPOlQIgzLK9yrnqMPcNo6g8= github.com/celestiaorg/merkletree v0.0.0-20230308153949-c33506a7aa26 h1:P2RI1xJ49EZ8cuHMcH+ZSBonfRDtBS8OS9Jdt1BWX3k= github.com/celestiaorg/merkletree v0.0.0-20230308153949-c33506a7aa26/go.mod h1:2m8ukndOegwB0PU0AfJCwDUQHqd7QQRlSXvQL5VToVY= -github.com/celestiaorg/nmt v0.22.1 h1:t7fqoP5MJ8mBns5DB2XjfcPxQpS3CKMkY+v+BEkDxYc= -github.com/celestiaorg/nmt v0.22.1/go.mod h1:ia/EpCk0enD5yO5frcxoNoFToz2Ghtk2i+blmCRjIY8= -github.com/celestiaorg/rsmt2d v0.13.1 h1:eRhp79DKTkDojwInKVs1lRK6f6zJc1BVlmZfUfI19yQ= -github.com/celestiaorg/rsmt2d v0.13.1/go.mod h1:P7t92OATXbBmc/P5uR+GCOBv+PV8wLb0vU32ucrb148= +github.com/celestiaorg/nmt v0.22.2 h1:JmOMtZL9zWAed1hiwb9DDs+ELcKp/ZQZ3rPverge/V8= +github.com/celestiaorg/nmt v0.22.2/go.mod h1:/7huDiSRL/d2EGhoiKctgSzmLOJoWG8yEfbFtY1+Mow= +github.com/celestiaorg/rsmt2d v0.14.0 h1:L7XJ3tRJDY8sQcvCjzHq0L7JmsmaSD+VItymIYFLqYc= +github.com/celestiaorg/rsmt2d v0.14.0/go.mod h1:4kxqiTdFev49sGiKXTDjohbWYOG5GlcIfftTgaBJnpc= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -390,21 +383,17 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= @@ -426,12 +415,16 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= -github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= -github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= @@ -441,8 +434,8 @@ github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkb github.com/coinbase/kryptology v1.8.0/go.mod h1:RYXOAPdzOGUe3qlSFkMGn58i3xUA8hmxYHksuq+8ciI= github.com/coinbase/rosetta-sdk-go v0.7.9 h1:lqllBjMnazTjIqYrOGv8h8jxjg9+hJazIGZr9ZvoCcA= github.com/coinbase/rosetta-sdk-go v0.7.9/go.mod h1:0/knutI7XGVqXmmH4OQD8OckFrbQ8yMsUZTG7FXCR2M= -github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo= -github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= +github.com/cometbft/cometbft-db v1.0.1 h1:SylKuLseMLQKw3+i8y8KozZyJcQSL98qEe2CGMCGTYE= +github.com/cometbft/cometbft-db v1.0.1/go.mod h1:EBrFs1GDRiTqrWXYi4v90Awf/gcdD5ExzdPbg4X8+mk= github.com/confio/ics23/go v0.9.1 h1:3MV46eeWwO3xCauKyAtuAdJYMyPnnchW4iLr2bTw6/U= github.com/confio/ics23/go v0.9.1/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= @@ -455,21 +448,18 @@ github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJ github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= -github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -481,8 +471,8 @@ github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRAp github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= -github.com/cosmos/gogoproto v1.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52o= -github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cosmos/iavl v0.19.6 h1:XY78yEeNPrEYyNCKlqr9chrwoeSDJ0bV2VjocTk//OU= @@ -499,15 +489,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= -github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 h1:ZFUue+PNxmHlu7pYv+IYMtqlaO/0VwaGEqKepZf9JpA= -github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= -github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= -github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cristalhq/jwt/v5 v5.4.0 h1:Wxi1TocFHaijyV608j7v7B9mPc4ZNjvWT3LKBO0d4QI= @@ -533,7 +518,6 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= @@ -541,24 +525,19 @@ github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= -github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/badger/v4 v4.2.1-0.20240106094458-1c417aa3799c h1:Z9rm0wkQBM+VF7vpyrbKnCcSbww0PKygLoptTpkX3d4= -github.com/dgraph-io/badger/v4 v4.2.1-0.20240106094458-1c417aa3799c/go.mod h1:T/uWAYxrXdaXw64ihI++9RMbKTCpKd/yE9+saARew7k= +github.com/dgraph-io/badger/v4 v4.3.0 h1:lcsCE1/1qrRhqP+zYx6xDZb8n7U+QlwNicpc676Ub40= +github.com/dgraph-io/badger/v4 v4.3.0/go.mod h1:Sc0T595g8zqAQRDf44n+z3wG4BOqLwceaFntt8KPxUM= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= -github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 h1:Pux6+xANi0I7RRo5E1gflI4EZ2yx3BGZ75JkAIvGEOA= +github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91/go.mod h1:swkazRqnUf1N62d0Nutz7KIj2UKqsm/H8tD0nBJAXqM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= -github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -607,10 +586,10 @@ github.com/etclabscore/go-openrpc-reflect v0.0.37/go.mod h1:0404Ky3igAasAOpyj1eE github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.10.17/go.mod h1:Lt5WzjM07XlXc95YzrhosmR4J9Ahd6X2wyEV2SvGhk0= -github.com/ethereum/go-ethereum v1.14.5 h1:szuFzO1MhJmweXjoM5nSAeDvjNUH3vIQoMzzQnfvjpw= -github.com/ethereum/go-ethereum v1.14.5/go.mod h1:VEDGGhSxY7IEjn98hJRFXl/uFvpRgbIIf2PpXiyGGgc= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= +github.com/ethereum/go-ethereum v1.14.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS6LvvxEY= +github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -626,10 +605,7 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/filecoin-project/go-jsonrpc v0.6.0 h1:/fFJIAN/k6EgY90m7qbyfY28woMwyseZmh2gVs5sYjY= github.com/filecoin-project/go-jsonrpc v0.6.0/go.mod h1:/n/niXcS4ZQua6i37LcVbY1TmlJR0UIK9mDFQq2ICek= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= -github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= @@ -641,19 +617,13 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= -github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= github.com/gammazero/deque v0.2.0 h1:SkieyNB4bg2/uZZLxvya0Pq6diUlwx7m2TeT7GAIWaA= github.com/gammazero/deque v0.2.0/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q= @@ -662,20 +632,18 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqG github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= -github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= -github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -694,9 +662,7 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -723,16 +689,13 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= -github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -741,11 +704,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -765,11 +725,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -813,8 +770,8 @@ github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -865,13 +822,11 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -915,7 +870,6 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -939,8 +893,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -981,9 +935,8 @@ github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoD github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -996,7 +949,6 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/arc/v2 v2.0.7 h1:QxkVTxwColcduO+LP7eJO56r2hFiG8zEbfAAzRv52KQ= github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5AmgmWNH1g+oFFVUHOEc= -github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -1012,8 +964,8 @@ github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8 github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= -github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= @@ -1028,7 +980,6 @@ github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6 github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -1049,168 +1000,45 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.22.0 h1:QTC+P5uhsBNq6HzX728nsLyFW6rYDeR/5hggf9YZX78= -github.com/ipfs/boxo v0.22.0/go.mod h1:yp1loimX0BDYOR0cyjtcXHv15muEh5V1FqO2QLlzykw= -github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= -github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= -github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= -github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= -github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= -github.com/ipfs/go-bitswap v0.1.8/go.mod h1:TOWoxllhccevbWFUR2N7B1MTSVVge1s6XSMiCSA4MzM= -github.com/ipfs/go-bitswap v0.3.4/go.mod h1:4T7fvNv/LmOys+21tnLzGKncMeeXUYUd1nUiJ2teMvI= -github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= -github.com/ipfs/go-bitswap v0.6.0/go.mod h1:Hj3ZXdOC5wBJvENtdqsixmzzRukqd8EHLxZLZc3mzRA= -github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= -github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk= -github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= -github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= -github.com/ipfs/go-block-format v0.1.1/go.mod h1:+McEIT+g52p+zz5xGAABGSOKrzmrdX97bc0USBdWPUs= -github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE= github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= -github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.1.4/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU= -github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= -github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= -github.com/ipfs/go-blockservice v0.5.0/go.mod h1:W6brZ5k20AehbmERplmERn8o2Ni3ZZubvAxaIUeaT6w= -github.com/ipfs/go-blockservice v0.5.2 h1:in9Bc+QcXwd1apOVM7Un9t8tixPKdaHQFdLSUM1Xgk8= -github.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk= -github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= -github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= -github.com/ipfs/go-cid v0.3.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= -github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= -github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= -github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= github.com/ipfs/go-datastore v0.5.1/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= -github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= -github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= -github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= -github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= -github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= github.com/ipfs/go-ds-badger4 v0.1.5 h1:MwrTsIUJIqH/ChuDdUOzxwxMxHx/Li1ECoSCKsCUxiA= github.com/ipfs/go-ds-badger4 v0.1.5/go.mod h1:LUU2FbhNdmhAbJmMeoahVRbe4GsduAODSJHWJJh2Vo4= -github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= -github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= -github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo= github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= -github.com/ipfs/go-fetcher v1.5.0/go.mod h1:5pDZ0393oRF/fHiLmtFZtpMNBQfHOYNPtryWedVuSWE= -github.com/ipfs/go-fetcher v1.6.1/go.mod h1:27d/xMV8bodjVs9pugh/RCjjK2OZ68UgAMspMdingNo= -github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= -github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= -github.com/ipfs/go-ipfs-blockstore v0.1.4/go.mod h1:Jxm3XMVjh6R17WvxFEiyKBLUGr86HgIYJW/D/MwqeYQ= -github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= -github.com/ipfs/go-ipfs-blockstore v1.1.2/go.mod h1:w51tNR9y5+QXB0wkNcHt4O2aSZjTdqaEWaQdSxEyUOY= -github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= -github.com/ipfs/go-ipfs-blockstore v1.3.0/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= -github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ= -github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= -github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= -github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= -github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= -github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8= -github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-ipfs-ds-help v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo= -github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= -github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= -github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= -github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= -github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM= -github.com/ipfs/go-ipfs-exchange-interface v0.1.0/go.mod h1:ych7WPlyHqFvCi/uQI48zLZuAWVP5iTQPXEfVaw5WEI= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0/go.mod h1:z6+RhJuDQbqKguVyslSOuVDhqF9JtTrO3eptSAiW2/Y= -github.com/ipfs/go-ipfs-exchange-interface v0.2.1 h1:jMzo2VhLKSHbVe+mHNzYgs95n0+t0Q69GQ5WhRDZV/s= -github.com/ipfs/go-ipfs-exchange-interface v0.2.1/go.mod h1:MUsYn6rKbG6CTtsDp+lKJPmVt3ZrCViNyH3rfPGsZ2E= -github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0= -github.com/ipfs/go-ipfs-exchange-offline v0.1.1/go.mod h1:vTiBRIbzSwDD0OWm+i3xeT0mO7jG2cbJYatp3HPk5XY= -github.com/ipfs/go-ipfs-exchange-offline v0.2.0/go.mod h1:HjwBeW0dvZvfOMwDP0TSKXIHf2s+ksdP4E3MLDRtLKY= -github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA= -github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s= -github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= -github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs= -github.com/ipfs/go-ipfs-files v0.3.0 h1:fallckyc5PYjuMEitPNrjRfpwl7YFt69heCOUhsbGxQ= -github.com/ipfs/go-ipfs-files v0.3.0/go.mod h1:xAUtYMwB+iu/dtf6+muHNSFQCJG2dSiStR2P6sn9tIM= -github.com/ipfs/go-ipfs-keystore v0.1.0/go.mod h1:LvLw7Qhnb0RlMOfCzK6OmyWxICip6lQ06CCmdbee75U= -github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= -github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= -github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= -github.com/ipfs/go-ipfs-redirects-file v0.1.1/go.mod h1:tAwRjCV0RjLTjH8DR/AU7VYvfQECg+lpUy2Mdzv7gyk= -github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= -github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= -github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= -github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo= -github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= -github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= -github.com/ipfs/go-ipld-cbor v0.0.6/go.mod h1:ssdxxaLJPXH7OjF5V4NSjBbcfh+evoR4ukuru0oPXMA= -github.com/ipfs/go-ipld-cbor v0.1.0 h1:dx0nS0kILVivGhfWuB6dUpMa/LAwElHPw1yOGYopoYs= -github.com/ipfs/go-ipld-cbor v0.1.0/go.mod h1:U2aYlmVrJr2wsUBU67K4KgepApSZddGRDWBYR0H4sCk= -github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= -github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= -github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= -github.com/ipfs/go-ipld-format v0.3.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-format v0.3.1/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-format v0.5.0/go.mod h1:ImdZqJQaEouMjCvqCe0ORUS+uoBmf7Hf+EO/jh+nk3M= github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U= github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg= -github.com/ipfs/go-ipld-legacy v0.1.0/go.mod h1:86f5P/srAmh9GcIcWQR9lfFLZPrIyyXQeVlOWeeWEuI= -github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk= github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM= -github.com/ipfs/go-ipns v0.2.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.1.0/go.mod h1:qX0d9h+wu53PFtCTXxdXVBakd6ZCvGDdkZUKmdLMLx0= -github.com/ipfs/go-libipfs v0.3.0/go.mod h1:pSUHZ5qPJTAidsxe9bAeHp3KIiw2ODEW2a2kM3v+iXI= -github.com/ipfs/go-libipfs v0.4.0/go.mod h1:XsU2cP9jBhDrXoJDe0WxikB8XcVmD3k2MEZvB3dbYu8= -github.com/ipfs/go-libipfs v0.6.0 h1:3FuckAJEm+zdHbHbf6lAyk0QUzc45LsFcGw102oBCZM= -github.com/ipfs/go-libipfs v0.6.0/go.mod h1:UjjDIuehp2GzlNP0HEr5I9GfFT7zWgst+YfpUEIThtw= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= -github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= @@ -1219,93 +1047,28 @@ github.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72g github.com/ipfs/go-log/v2 v2.5.0/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= -github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= -github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= -github.com/ipfs/go-merkledag v0.6.0/go.mod h1:9HSEwRd5sV+lbykiYP+2NC/3o6MZbKNaa4hfNcH5iH0= -github.com/ipfs/go-merkledag v0.9.0/go.mod h1:bPHqkHt5OZ0p1n3iqPeDiw2jIBkjAytRjS3WSBwjq90= -github.com/ipfs/go-merkledag v0.10.0/go.mod h1:zkVav8KiYlmbzUzNM6kENzkdP5+qR7+2mCwxkQ6GIj8= -github.com/ipfs/go-merkledag v0.11.0 h1:DgzwK5hprESOzS4O1t/wi6JDpyVQdvm9Bs59N/jqfBY= -github.com/ipfs/go-merkledag v0.11.0/go.mod h1:Q4f/1ezvBiJV0YCIXvt51W/9/kqJGH4I1LsA7+djsM4= github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-metrics-prometheus v0.0.2 h1:9i2iljLg12S78OhC6UAiXi176xvQGiZaGVF1CUVdE+s= github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks= -github.com/ipfs/go-namesys v0.7.0/go.mod h1:KYSZBVZG3VJC34EfqqJPG7T48aWgxseoMPAPA5gLyyQ= -github.com/ipfs/go-path v0.1.1/go.mod h1:vC8q4AKOtrjJz2NnllIrmr2ZbGlF5fW2OKKyhV9ggb0= -github.com/ipfs/go-path v0.3.0/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= -github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.1.1/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUnr+P7jivavs9lY= -github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= -github.com/ipfs/go-peertaskqueue v0.8.0/go.mod h1:cz8hEnnARq4Du5TGqiWKgMr/BOSQ5XOgMOh1K5YYKKM= github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= github.com/ipfs/go-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew= github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI= -github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= -github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= -github.com/ipfs/go-unixfs v0.4.3/go.mod h1:TSG7G1UuT+l4pNj91raXAPkX0BhJi3jST1FDTfQ5QyM= -github.com/ipfs/go-unixfs v0.4.4/go.mod h1:TSG7G1UuT+l4pNj91raXAPkX0BhJi3jST1FDTfQ5QyM= -github.com/ipfs/go-unixfs v0.4.5 h1:wj8JhxvV1G6CD7swACwSKYa+NgtdWC1RUit+gFnymDU= -github.com/ipfs/go-unixfs v0.4.5/go.mod h1:BIznJNvt/gEx/ooRMI4Us9K8+qeGO7vx1ohnbk8gjFg= -github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s= -github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo= -github.com/ipfs/go-unixfsnode v1.5.1/go.mod h1:ed79DaG9IEuZITJVQn4U6MZDftv6I3ygUBLPfhEbHvk= -github.com/ipfs/go-unixfsnode v1.5.2/go.mod h1:NlOebRwYx8lMCNMdhAhEspYPBD3obp7TE0LvBqHY+ks= -github.com/ipfs/go-unixfsnode v1.7.1/go.mod h1:PVfoyZkX1B34qzT3vJO4nsLUpRCyhnMuHBznRcXirlk= -github.com/ipfs/go-unixfsnode v1.9.0 h1:ubEhQhr22sPAKO2DNsyVBW7YB/zA8Zkif25aBvz8rc8= -github.com/ipfs/go-unixfsnode v1.9.0/go.mod h1:HxRu9HYHOjK6HUqFBAi++7DVoWAHn0o4v/nZ/VA+0g8= -github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= -github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= -github.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs= -github.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw= -github.com/ipfs/interface-go-ipfs-core v0.9.0/go.mod h1:F3EcmDy53GFkF0H3iEJpfJC320fZ/4G60eftnItrrJ0= -github.com/ipfs/interface-go-ipfs-core v0.10.0/go.mod h1:F3EcmDy53GFkF0H3iEJpfJC320fZ/4G60eftnItrrJ0= -github.com/ipld/go-car v0.5.0/go.mod h1:ppiN5GWpjOZU9PgpAZ9HbZd9ZgSpwPMr48fGRJOWmvE= -github.com/ipld/go-car v0.6.2 h1:Hlnl3Awgnq8icK+ze3iRghk805lu8YNq3wlREDTF2qc= -github.com/ipld/go-car v0.6.2/go.mod h1:oEGXdwp6bmxJCZ+rARSkDliTeYnVzv3++eXajZ+Bmr8= -github.com/ipld/go-car/v2 v2.1.1/go.mod h1:+2Yvf0Z3wzkv7NeI69i8tuZ+ft7jyjPYIWZzeVNeFcI= -github.com/ipld/go-car/v2 v2.5.1/go.mod h1:jKjGOqoCj5zn6KjnabD6JbnCsMntqU2hLiU6baZVO3E= -github.com/ipld/go-car/v2 v2.8.0/go.mod h1:a+BnAxUqgr7wcWxW/lI6ctyEQ2v9gjBChPytwFMp2f4= -github.com/ipld/go-car/v2 v2.10.1/go.mod h1:sQEkXVM3csejlb1kCCb+vQ/pWBKX9QtvsrysMQjOgOg= -github.com/ipld/go-car/v2 v2.13.1 h1:KnlrKvEPEzr5IZHKTXLAEub+tPrzeAFQVRlSQvuxBO4= -github.com/ipld/go-car/v2 v2.13.1/go.mod h1:QkdjjFNGit2GIkpQ953KBwowuoukoM75nP/JI1iDJdo= -github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA= -github.com/ipld/go-codec-dagpb v1.3.1/go.mod h1:ErNNglIi5KMur/MfFE/svtgQthzVvf+43MrzLbpcIZY= -github.com/ipld/go-codec-dagpb v1.4.1/go.mod h1:XdXTO/TUD/ra9RcK/NfmwBfr1JpFxM2uRKaB9oe4LxE= -github.com/ipld/go-codec-dagpb v1.5.0/go.mod h1:0yRIutEFD8o1DGVqw4RSHh+BUTlJA9XWldxaaWR/o4g= github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= -github.com/ipld/go-ipld-prime v0.9.0/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= -github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8= -github.com/ipld/go-ipld-prime v0.14.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM= -github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA= -github.com/ipld/go-ipld-prime v0.18.0/go.mod h1:735yXW548CKrLwVCYXzqx90p5deRJMVVxM9eJ4Qe+qE= -github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4= -github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= -github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY= -github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo= -github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd/go.mod h1:wZ8hH8UxeryOs4kJEJaiui/s00hDSbE37OKsL47g+Sw= -github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= -github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= -github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jellydator/ttlcache/v2 v2.11.1/go.mod h1:RtE5Snf0/57e+2cLWFYWCCsLas2Hy3c5Z4n14XmSvTI= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= @@ -1328,12 +1091,10 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -1351,21 +1112,13 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= @@ -1374,17 +1127,14 @@ github.com/klauspost/reedsolomon v1.12.1 h1:NhWgum1efX1x58daOBGCFWcxtEhOhXKKl1HA github.com/klauspost/reedsolomon v1.12.1/go.mod h1:nEi5Kjb6QqtbofI6s+cbG/j1da11c96IBYBSnVGtuBs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/koron/go-ssdp v0.0.2/go.mod h1:XoLfkAiA2KeZsYh4DbHxD7h3nR2AZNqVQOa+LJuqPYs= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1398,13 +1148,10 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= -github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= @@ -1412,283 +1159,122 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= -github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= -github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= github.com/libp2p/go-conn-security-multistream v0.3.0/go.mod h1:EEP47t4fw/bTelVmEzIDqSe69hO/ip52xBEhZMLWAHM= -github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= -github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= -github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= -github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= -github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k= -github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= -github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= -github.com/libp2p/go-libp2p v0.13.0/go.mod h1:pM0beYdACRfHO1WcJlp65WXyG2A6NqYM+t2DTVAJxMo= -github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= +github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= github.com/libp2p/go-libp2p v0.19.0/go.mod h1:Ki9jJXLO2YqrTIFxofV7Twyd3INWPT97+r8hGt7XPjI= -github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4= -github.com/libp2p/go-libp2p v0.23.4/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI= -github.com/libp2p/go-libp2p v0.25.0/go.mod h1:vXHmFpcfl+xIGN4qW58Bw3a0/SKGAesr5/T4IuJHE3o= -github.com/libp2p/go-libp2p v0.25.1/go.mod h1:xnK9/1d9+jeQCVvi/f1g12KqtVi/jP/SijtKV1hML3g= -github.com/libp2p/go-libp2p v0.36.2 h1:BbqRkDaGC3/5xfaJakLV/BrpjlAuYqSB0lRvtzL3B/U= -github.com/libp2p/go-libp2p v0.36.2/go.mod h1:XO3joasRE4Eup8yCTTP/+kX+g92mOgRaadk46LmPhHY= +github.com/libp2p/go-libp2p v0.37.2 h1:Irh+n9aDPTLt9wJYwtlHu6AhMUipbC1cGoJtOiBqI9c= +github.com/libp2p/go-libp2p v0.37.2/go.mod h1:M8CRRywYkqC6xKHdZ45hmqVckBj5z4mRLIMLWReypz8= github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I= -github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= -github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= -github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= -github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= -github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= -github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= -github.com/libp2p/go-libp2p-autonat v0.4.2/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= -github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= -github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= github.com/libp2p/go-libp2p-blankhost v0.3.0/go.mod h1:urPC+7U01nCGgJ3ZsV8jdwTp6Ji9ID0dMTvq+aJ+nZU= -github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= -github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= -github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= -github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= github.com/libp2p/go-libp2p-circuit v0.6.0/go.mod h1:kB8hY+zCpMeScyvFrKrGicRdid6vNXbunKE4rXATZ0M= -github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= -github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= -github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= -github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= -github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= -github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= -github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= -github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.6/go.mod h1:dgHr0l0hIKfWpGpqAMbpo19pen9wJfdCGv51mTmdpmM= github.com/libp2p/go-libp2p-core v0.10.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= github.com/libp2p/go-libp2p-core v0.11.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= github.com/libp2p/go-libp2p-core v0.12.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= github.com/libp2p/go-libp2p-core v0.14.0/go.mod h1:tLasfcVdTXnixsLB0QYaT1syJOhsbrhG7q6pGrHtBg8= github.com/libp2p/go-libp2p-core v0.15.1/go.mod h1:agSaboYM4hzB1cWekgVReqV5M4g5M+2eNNejV+1EEhs= -github.com/libp2p/go-libp2p-core v0.19.0/go.mod h1:AkA+FUKQfYt1FLNef5fOPlo/naAWjKy/RCjkcPjqzYg= -github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= -github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= -github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= -github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-kad-dht v0.19.0/go.mod h1:qPIXdiZsLczhV4/+4EO1jE8ae0YCW4ZOogc4WVIyTEU= -github.com/libp2p/go-libp2p-kad-dht v0.21.0/go.mod h1:Bhm9diAFmc6qcWAr084bHNL159srVZRKADdp96Qqd1I= -github.com/libp2p/go-libp2p-kad-dht v0.26.1 h1:AazV3LCImYVkDUGAHx5lIEgZ9iUI2QQKH5GMRQU8uEA= -github.com/libp2p/go-libp2p-kad-dht v0.26.1/go.mod h1:mqRUGJ/+7ziQ3XknU2kKHfsbbgb9xL65DXjPOJwmZF8= -github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= -github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= -github.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9GiSErbQQo0= -github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0= -github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= -github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= -github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= -github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= -github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= -github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= +github.com/libp2p/go-libp2p-kad-dht v0.27.0 h1:1Ea32tVTPiAfaLpPMbaBWFJgbsi/JpMqC2YBuFdf32o= +github.com/libp2p/go-libp2p-kad-dht v0.27.0/go.mod h1:ixhjLuzaXSGtWsKsXTj7erySNuVC4UP7NO015cRrF14= +github.com/libp2p/go-libp2p-kbucket v0.6.4 h1:OjfiYxU42TKQSB8t8WYd8MKhYhMJeO2If+NiuKfb6iQ= +github.com/libp2p/go-libp2p-kbucket v0.6.4/go.mod h1:jp6w82sczYaBsAypt5ayACcRJi0lgsba7o4TzJKEfWA= github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= github.com/libp2p/go-libp2p-mplex v0.5.0/go.mod h1:eLImPJLkj3iG5t5lq68w3Vm5NAQ5BcKwrrb2VmOYb3M= -github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= -github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= github.com/libp2p/go-libp2p-nat v0.1.0/go.mod h1:DQzAG+QbDYjN1/C3B6vXucLtz3u9rEonLVPtZVzQqks= -github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= -github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM= -github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= github.com/libp2p/go-libp2p-noise v0.4.0/go.mod h1:BzzY5pyzCYSyJbQy9oD8z5oP2idsafjt4/X42h9DjZU= -github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= -github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= -github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= -github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= -github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-peerstore v0.4.0/go.mod h1:rDJUFyzEWPpXpEwywkcTYYzDHlwza8riYMaUzaN6hX0= github.com/libp2p/go-libp2p-peerstore v0.6.0/go.mod h1:DGEmKdXrcYpK9Jha3sS7MhqYdInxJy84bIPtSu65bKc= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-pubsub v0.11.0 h1:+JvS8Kty0OiyUiN0i8H5JbaCgjnJTRnTHe4rU88dLFc= -github.com/libp2p/go-libp2p-pubsub v0.11.0/go.mod h1:QEb+hEV9WL9wCiUAnpY29FZR6W3zK8qYlaml8R4q6gQ= -github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= +github.com/libp2p/go-libp2p-pubsub v0.12.0 h1:PENNZjSfk8KYxANRlpipdS7+BfLmOl3L2E/6vSNjbdI= +github.com/libp2p/go-libp2p-pubsub v0.12.0/go.mod h1:Oi0zw9aw8/Y5GC99zt+Ef2gYAl+0nZlwdJonDyOz/sE= github.com/libp2p/go-libp2p-quic-transport v0.13.0/go.mod h1:39/ZWJ1TW/jx1iFkKzzUg00W6tDJh73FC0xYudjr7Hc= github.com/libp2p/go-libp2p-quic-transport v0.16.0/go.mod h1:1BXjVMzr+w7EkPfiHkKnwsWjPjtfaNT0q8RS3tGDvEQ= github.com/libp2p/go-libp2p-quic-transport v0.17.0/go.mod h1:x4pw61P3/GRCcSLypcQJE/Q2+E9f4X+5aRcZLXf20LM= -github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= github.com/libp2p/go-libp2p-resource-manager v0.2.1/go.mod h1:K+eCkiapf+ey/LADO4TaMpMTP9/Qde/uLlrnRqV4PLQ= -github.com/libp2p/go-libp2p-routing-helpers v0.4.0/go.mod h1:dYEAgkVhqho3/YKxfOEGdFMIcWfAFNlZX8iAIihYA2E= github.com/libp2p/go-libp2p-routing-helpers v0.7.4 h1:6LqS1Bzn5CfDJ4tzvP9uwh42IB7TJLNFJA6dEeGBv84= github.com/libp2p/go-libp2p-routing-helpers v0.7.4/go.mod h1:we5WDj9tbolBXOuF1hGOkR+r7Uh1408tQbAKaT5n1LE= -github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= -github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= -github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= -github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= -github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= -github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= -github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= -github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= -github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= -github.com/libp2p/go-libp2p-swarm v0.4.0/go.mod h1:XVFcO52VoLoo0eitSxNQWYq4D6sydGOweTOAjJNraCw= -github.com/libp2p/go-libp2p-swarm v0.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= github.com/libp2p/go-libp2p-swarm v0.8.0/go.mod h1:sOMp6dPuqco0r0GHTzfVheVBh6UEL0L1lXUZ5ot2Fvc= github.com/libp2p/go-libp2p-swarm v0.10.0/go.mod h1:71ceMcV6Rg/0rIQ97rsZWMzto1l9LnNquef+efcRbmA= github.com/libp2p/go-libp2p-swarm v0.10.2/go.mod h1:Pdkq0QU5a+qu+oyqIV3bknMsnzk9lnNyKvB9acJ5aZs= -github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= -github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= github.com/libp2p/go-libp2p-testing v0.5.0/go.mod h1:QBk8fqIL1XNcno/l3/hhaIEn4aLRijpYOR+zVjjlh+A= github.com/libp2p/go-libp2p-testing v0.7.0/go.mod h1:OLbdn9DbgdMwv00v+tlp1l3oe2Cl+FAjoWIA2pa0X6E= github.com/libp2p/go-libp2p-testing v0.9.0/go.mod h1:Td7kbdkWqYTJYQGTwzlgXwaqldraIanyjuRiAbK/XQU= github.com/libp2p/go-libp2p-testing v0.9.2/go.mod h1:Td7kbdkWqYTJYQGTwzlgXwaqldraIanyjuRiAbK/XQU= -github.com/libp2p/go-libp2p-testing v0.11.0/go.mod h1:qG4sF27dfKFoK9KlVzK2y52LQKhp0VEmLjV5aDqr1Hg= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= -github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= github.com/libp2p/go-libp2p-tls v0.3.0/go.mod h1:fwF5X6PWGxm6IDRwF3V8AVCCj/hOd5oFlg+wo2FxJDY= github.com/libp2p/go-libp2p-tls v0.4.1/go.mod h1:EKCixHEysLNDlLUoKxv+3f/Lp90O2EXNjTr0UQDnrIw= -github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= -github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= -github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= -github.com/libp2p/go-libp2p-transport-upgrader v0.4.0/go.mod h1:J4ko0ObtZSmgn5BX5AmegP+dK3CSnU2lMCKsSq/EY0s= -github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= github.com/libp2p/go-libp2p-transport-upgrader v0.5.0/go.mod h1:Rc+XODlB3yce7dvFV4q/RmyJGsFcCZRkeZMu/Zdg0mo= github.com/libp2p/go-libp2p-transport-upgrader v0.7.0/go.mod h1:GIR2aTRp1J5yjVlkUoFqMkdobfob6RnAwYg/RZPhrzg= github.com/libp2p/go-libp2p-transport-upgrader v0.7.1/go.mod h1:GIR2aTRp1J5yjVlkUoFqMkdobfob6RnAwYg/RZPhrzg= -github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= -github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= -github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= -github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= -github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= -github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= -github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= -github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= -github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4= -github.com/libp2p/go-libp2p-yamux v0.5.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE= github.com/libp2p/go-libp2p-yamux v0.8.0/go.mod h1:yTkPgN2ib8FHyU1ZcVD7aelzyAqXXwEPbyx+aSKm9h8= github.com/libp2p/go-libp2p-yamux v0.8.1/go.mod h1:rUozF8Jah2dL9LLGyBaBeTQeARdwhefMCTQVQt6QobE= github.com/libp2p/go-libp2p-yamux v0.9.1/go.mod h1:wRc6wvyxQINFcKe7daL4BeQ02Iyp+wxyC8WCNfngBrA= -github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= -github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= -github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= -github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= github.com/libp2p/go-mplex v0.4.0/go.mod h1:y26Lx+wNVtMYMaPu300Cbot5LkEZ4tJaNYeHeT9dh6E= -github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= -github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= -github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= -github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= -github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= -github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= -github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= github.com/libp2p/go-reuseport v0.1.0/go.mod h1:bQVn9hmfcTaoo0c9v5pBhOarsU1eNOBZdaAd2hzXRKU= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= -github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= -github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= -github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= github.com/libp2p/go-reuseport-transport v0.1.0/go.mod h1:vev0C0uMkzriDY59yFHD9v+ujJvYmDQVLowvAjEOmfw= github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= -github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= github.com/libp2p/go-stream-muxer-multistream v0.4.0/go.mod h1:nb+dGViZleRP4XcyHuZSVrJCBl55nRBOMmiSL/dyziw= -github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= -github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= -github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= -github.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M= -github.com/libp2p/go-tcp-transport v0.2.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= github.com/libp2p/go-tcp-transport v0.4.0/go.mod h1:0y52Rwrn4076xdJYu/51/qJIdxz+EWDAOG2S45sV3VI= github.com/libp2p/go-tcp-transport v0.5.0/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y= github.com/libp2p/go-tcp-transport v0.5.1/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y= -github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= -github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= -github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= -github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= -github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= github.com/libp2p/go-ws-transport v0.6.0/go.mod h1:dXqtI9e2JV9FtF1NOtWVZSKXh5zXvnuwPXfj8GPBbYU= -github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U= -github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= github.com/libp2p/go-yamux/v3 v3.0.1/go.mod h1:s2LsDhHbh+RfCsQoICSYt58U2f8ijtPANFD8BmE74Bo= github.com/libp2p/go-yamux/v3 v3.0.2/go.mod h1:s2LsDhHbh+RfCsQoICSYt58U2f8ijtPANFD8BmE74Bo= github.com/libp2p/go-yamux/v3 v3.1.1/go.mod h1:jeLEQgLXqE2YqX1ilAClIfCMDY+0uXQUKmmb/qp0gT4= -github.com/libp2p/go-yamux/v3 v3.1.2/go.mod h1:jeLEQgLXqE2YqX1ilAClIfCMDY+0uXQUKmmb/qp0gT4= -github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/libp2p/zeroconf/v2 v2.1.1/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= -github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= +github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik= +github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0= github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg= github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= -github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= -github.com/lucas-clemente/quic-go v0.29.1/go.mod h1:CTcNfLYJS2UuRNB+zcNlgvkjBhxX6Hm3WUxxAQx2mgE= github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= @@ -1706,22 +1292,15 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8= github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI= github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/marten-seemann/webtransport-go v0.1.1/go.mod h1:kBEh5+RSvOA4troP1vyOVBWK4MIMzDICXVrvCPrYcrM= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -1740,33 +1319,26 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= -github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -1777,8 +1349,8 @@ github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjK github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= @@ -1808,15 +1380,12 @@ github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iP github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -1831,10 +1400,7 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= -github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= @@ -1845,41 +1411,19 @@ github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9x github.com/multiformats/go-multiaddr v0.4.0/go.mod h1:YcpyLH8ZPudLxQlemYBPhSm0/oCXAT8Z4mzFpyoPyRc= github.com/multiformats/go-multiaddr v0.4.1/go.mod h1:3afI9HfVW8csiF8UZqtpYRiDyew8pRX7qLIGHu9FLuM= github.com/multiformats/go-multiaddr v0.5.0/go.mod h1:3KAxNkUqLTJ20AAwN4XVX4kZar+bR+gh4zgbfr3SNug= -github.com/multiformats/go-multiaddr v0.6.0/go.mod h1:F4IpaKZuPP360tOMn2Tpyu0At8w23aRyVqeK0DbFeGM= -github.com/multiformats/go-multiaddr v0.7.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= -github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ= github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= -github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= -github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= -github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= -github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= -github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= -github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= -github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues= -github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= -github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= -github.com/multiformats/go-multicodec v0.8.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= -github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= @@ -1890,22 +1434,14 @@ github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUj github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= -github.com/multiformats/go-multihash v0.2.0/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= -github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= github.com/multiformats/go-multistream v0.2.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= -github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= github.com/multiformats/go-multistream v0.3.0/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= github.com/multiformats/go-multistream v0.3.1/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= -github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= -github.com/multiformats/go-multistream v0.4.0/go.mod h1:BS6ZSYcA4NwYEaIMeCtpJydp2Dc+fNRA6uJMSu/m8+4= -github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= -github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= -github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= +github.com/multiformats/go-multistream v0.6.0 h1:ZaHKbsL404720283o4c/IHQXiS6gb8qAN5EIJ4PN5EA= +github.com/multiformats/go-multistream v0.6.0/go.mod h1:MOyoG5otO24cHIg8kf9QW2/NozURlkP/rvi2FQJyCPg= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= @@ -1944,7 +1480,6 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= @@ -1952,37 +1487,23 @@ github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= -github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0= -github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= -github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 h1:CznVS40zms0Dj5he4ERo+fRPtO0qxUk8lA8Xu3ddet0= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333/go.mod h1:Ag6rSXkHIckQmjFBCweJEEt1mrTPBv8b9W4aU/NQWfI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -2011,11 +1532,9 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2D github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= -github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= @@ -2025,15 +1544,17 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pion/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo= -github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA= +github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/ice/v2 v2.3.34 h1:Ic1ppYCj4tUOcPAp76U6F3fVrlSw8A9JtRXLqw6BbUM= -github.com/pion/ice/v2 v2.3.34/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ= -github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= -github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= +github.com/pion/ice/v2 v2.3.36 h1:SopeXiVbbcooUg2EIR8sq4b13RQ8gzrkkldOVg+bBsc= +github.com/pion/ice/v2 v2.3.36/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ= +github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= +github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= @@ -2044,10 +1565,10 @@ github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9 github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/rtp v1.8.8 h1:EtYFHI0rpUEjT/RMnGfb1vdJhbYmPG77szD72uUnSxs= -github.com/pion/rtp v1.8.8/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/sctp v1.8.20 h1:sOc3lkV/tQaP57ZUEXIMdM2V92IIB2ia5v/ygnBxaEg= -github.com/pion/sctp v1.8.20/go.mod h1:oTxw8i5m+WbDHZJL/xUpe6CPIn1Y0GIKKwTLF4h53H8= +github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk= +github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw= +github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk= @@ -2060,13 +1581,13 @@ github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLh github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= -github.com/pion/transport/v3 v3.0.6 h1:k1mQU06bmmX143qSWgXFqSH1KUJceQvIUuVH/K5ELWw= -github.com/pion/transport/v3 v3.0.6/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc= github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.3.0 h1:Rf4u6n6U5t5sUxhYPQk/samzU/oDv7jk6BA5hyO2F9I= -github.com/pion/webrtc/v3 v3.3.0/go.mod h1:hVmrDJvwhEertRWObeb1xzulzHGeVUoPlWvxdGzcfU0= +github.com/pion/webrtc/v3 v3.3.4 h1:v2heQVnXTSqNRXcaFQVOhIOYkLMxOu1iJG8uy1djvkk= +github.com/pion/webrtc/v3 v3.3.4/go.mod h1:liNa+E1iwyzyXqNUwvoMRNQ10x8h8FOeJKL8RkIbamE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -2077,10 +1598,6 @@ github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUI github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -2096,17 +1613,14 @@ github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66Id github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -2123,9 +1637,8 @@ github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16 github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -2137,21 +1650,15 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc= -github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo= -github.com/quic-go/quic-go v0.45.2 h1:DfqBmqjb4ExSdxRIb/+qXhPC+7k6+DUNZha4oeiC9fY= -github.com/quic-go/quic-go v0.45.2/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= -github.com/quic-go/webtransport-go v0.5.1/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= -github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg= -github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= +github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/raulk/clock v1.1.0/go.mod h1:3MpVxdZ/ODBQDxbN+kzshf5OSZwPjtMDx6BBXBmOeY0= @@ -2172,18 +1679,15 @@ github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRr github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rollkit/go-da v0.5.0 h1:sQpZricNS+2TLx3HMjNWhtRfqtvVC/U4pWHpfUz3eN4= -github.com/rollkit/go-da v0.5.0/go.mod h1:VsUeAoPvKl4Y8wWguu/VibscYiFFePkkrvZWyTjZHww= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rollkit/go-da v0.8.0 h1:oJKojC421eRC4mNqbujf40GzLFNp7HapgeB7Z/r0tyc= +github.com/rollkit/go-da v0.8.0/go.mod h1:3eHWK5gkv8lhwq6bjOZOi82WwHyS2B9rQOlUrE1GGws= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= @@ -2192,7 +1696,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -2228,6 +1731,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -2235,11 +1739,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= @@ -2297,32 +1798,28 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= -github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/tidwall/btree v1.5.0 h1:iV0yVY/frd7r6qGBXfEYs7DH0gTDgrKTrDjS7xt/IyQ= github.com/tidwall/btree v1.5.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -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.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -2333,7 +1830,6 @@ github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= -github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -2347,13 +1843,9 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= -github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -2371,39 +1863,19 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= -github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-testmark v0.10.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-testmark v0.11.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= -github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= -github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= -github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= -github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= -github.com/whyrusleeping/cbor-gen v0.0.0-20221220214510-0333c149dec0/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/cbor-gen v0.1.2 h1:WQFlrPhpcQl+M2/3dP5cvlTLWPVsL6LGBb9jJt6l/cA= -github.com/whyrusleeping/cbor-gen v0.1.2/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= -github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= -github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= -github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= -github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= -github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= -github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= @@ -2428,15 +1900,14 @@ gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975/go.mod h1:ZkMZ gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs= gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40/go.mod h1:rOnSnoRyxMI3fe/7KIbVcsHRGxe30OONv8dEgo+vCfA= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= +go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -2446,41 +1917,32 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0 h1:2JydY5UiDpqvj2p7sO9bgHuhTy4hgTZ0ymehdq/Ob0Q= go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0/go.mod h1:ch3a5QxOqVWxas4CzjCFFOOQe+7HgAXC/N1oVxS9DK4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel v1.13.0/go.mod h1:FH3RtdZCzRkJYFTCsAKDy9l/XYjMdNv6QrkFFB8DvVg= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 h1:/0YaXu3755A/cFbtXp+21lkXgI0QE5avTWA2HjU9/WE= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0/go.mod h1:m7SFxp0/7IxmJPLIY3JhOcU9CoFzDaCPL6xxQIxhA+o= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.opentelemetry.io/otel/trace v1.13.0/go.mod h1:muCvmmO9KKpvuXSf3KKAXXB2ygNYHQ+ZfI5X08d3tds= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= @@ -2490,31 +1952,23 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM= -go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= -go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= -go.uber.org/fx v1.22.1 h1:nvvln7mwyT5s1q201YE29V/BFrGor6vMiDNpU/78Mys= -go.uber.org/fx v1.22.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= +go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= +go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= @@ -2526,9 +1980,6 @@ go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= @@ -2539,13 +1990,10 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -2555,7 +2003,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -2565,29 +2012,21 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -2596,15 +2035,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= -golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= -golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230129154200-a960b3787bd2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -2623,26 +2055,20 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2663,16 +2089,13 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -2721,24 +2144,16 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2766,8 +2181,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2784,8 +2199,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2797,12 +2212,10 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2810,10 +2223,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2821,7 +2231,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2855,7 +2264,6 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2890,7 +2298,6 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2901,28 +2308,18 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2932,22 +2329,20 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2958,14 +2353,12 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3003,12 +2396,10 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -3036,7 +2427,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -3044,14 +2434,11 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3060,8 +2447,8 @@ golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= @@ -3246,10 +2633,10 @@ google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 h1:MuYw1wJzT+ZkybKfaOXKp5hJiZDn2iHaXRw0mRYdHSc= -google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4/go.mod h1:px9SlOOZBg1wM1zdnr8jEL4CNGUBZ+ZKYtNPApNQc4c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 h1:Di6ANFilr+S60a4S61ZM00vLdw0IrQOSMS2/6mrnOU0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -3295,8 +2682,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -3313,13 +2700,12 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -3353,7 +2739,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -3373,8 +2758,8 @@ lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= +nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/header/header.go b/header/header.go index a014864551..4db80d0ac3 100644 --- a/header/header.go +++ b/header/header.go @@ -11,8 +11,8 @@ import ( "github.com/tendermint/tendermint/light" core "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/da" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/rsmt2d" ) @@ -201,7 +201,7 @@ func (eh *ExtendedHeader) Verify(untrst *ExtendedHeader) error { if err := eh.ValidatorSet.VerifyCommitLightTrusting(eh.ChainID(), untrst.Commit, light.DefaultTrustLevel); err != nil { return &libhead.VerifyError{ Reason: fmt.Errorf("%w: %w", ErrVerifyCommitLightTrustingFailed, err), - SoftFailure: true, + SoftFailure: core.IsErrNotEnoughVotingPowerSigned(err), } } return nil diff --git a/header/headertest/fraud/testing.go b/header/headertest/fraud/testing.go index 7a876c7ecf..546da7dd58 100644 --- a/header/headertest/fraud/testing.go +++ b/header/headertest/fraud/testing.go @@ -5,21 +5,20 @@ import ( "testing" "time" - "github.com/ipfs/boxo/blockservice" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/bytes" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/da" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/header/headertest" - "github.com/celestiaorg/celestia-node/share/eds" "github.com/celestiaorg/celestia-node/share/eds/edstest" "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/store" ) // FraudMaker allows to produce an invalid header at the specified height in order to produce the @@ -45,7 +44,7 @@ func NewFraudMaker(t *testing.T, height int64, vals []types.PrivValidator, valSe } } -func (f *FraudMaker) MakeExtendedHeader(odsSize int, edsStore *eds.Store) header.ConstructFn { +func (f *FraudMaker) MakeExtendedHeader(odsSize int, edsStore *store.Store) header.ConstructFn { return func( h *types.Header, comm *types.Commit, @@ -58,14 +57,14 @@ func (f *FraudMaker) MakeExtendedHeader(odsSize int, edsStore *eds.Store) header hdr := *h if h.Height == f.height { - adder := ipld.NewProofsAdder(odsSize) + adder := ipld.NewProofsAdder(odsSize, false) square := edstest.RandByzantineEDS(f.t, odsSize, nmt.NodeVisitor(adder.VisitFn())) dah, err := da.NewDataAvailabilityHeader(square) require.NoError(f.t, err) hdr.DataHash = dah.Hash() ctx := ipld.CtxWithProofsAdder(context.Background(), adder) - require.NoError(f.t, edsStore.Put(ctx, h.DataHash.Bytes(), square)) + require.NoError(f.t, edsStore.PutODSQ4(ctx, &dah, uint64(h.Height), square)) *eds = *square } @@ -89,11 +88,8 @@ func (f *FraudMaker) MakeExtendedHeader(odsSize int, edsStore *eds.Store) header func CreateFraudExtHeader( t *testing.T, eh *header.ExtendedHeader, - serv blockservice.BlockService, ) *header.ExtendedHeader { square := edstest.RandByzantineEDS(t, len(eh.DAH.RowRoots)) - err := ipld.ImportEDS(context.Background(), square, serv) - require.NoError(t, err) dah, err := da.NewDataAvailabilityHeader(square) require.NoError(t, err) eh.DAH = &dah diff --git a/header/headertest/testing.go b/header/headertest/testing.go index 245288b8c5..2e25592f27 100644 --- a/header/headertest/testing.go +++ b/header/headertest/testing.go @@ -15,9 +15,8 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/proto/tendermint/version" "github.com/tendermint/tendermint/types" - tmtime "github.com/tendermint/tendermint/types/time" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/da" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/go-header/headertest" "github.com/celestiaorg/rsmt2d" @@ -40,6 +39,7 @@ type TestSuite struct { // blockTime is optional - if set, the test suite will generate // blocks timestamped at the specified interval blockTime time.Duration + startTime time.Time } func NewStore(t *testing.T) libhead.Store[*header.ExtendedHeader] { @@ -62,11 +62,23 @@ func NewTestSuite(t *testing.T, numValidators int, blockTime time.Duration) *Tes vals: vals, valSet: valSet, blockTime: blockTime, + startTime: time.Now(), + } +} + +func NewTestSuiteWithGenesisTime(t *testing.T, startTime time.Time, blockTime time.Duration) *TestSuite { + valSet, vals := RandValidatorSet(3, 1) + return &TestSuite{ + t: t, + vals: vals, + valSet: valSet, + blockTime: blockTime, + startTime: startTime, } } func (s *TestSuite) genesis() *header.ExtendedHeader { - dah := share.EmptyRoot() + dah := share.EmptyEDSRoots() gen := RandRawHeader(s.t) @@ -74,10 +86,11 @@ func (s *TestSuite) genesis() *header.ExtendedHeader { gen.ValidatorsHash = s.valSet.Hash() gen.NextValidatorsHash = s.valSet.Hash() gen.Height = 1 + gen.Time = s.startTime voteSet := types.NewVoteSet(gen.ChainID, gen.Height, 0, tmproto.PrecommitType, s.valSet) blockID := RandBlockID(s.t) blockID.Hash = gen.Hash() - commit, err := MakeCommit(blockID, gen.Height, 0, voteSet, s.vals, time.Now()) + commit, err := MakeCommit(blockID, gen.Height, 0, voteSet, s.vals, s.startTime) require.NoError(s.t, err) eh := &header.ExtendedHeader{ @@ -152,7 +165,7 @@ func (s *TestSuite) NextHeader() *header.ExtendedHeader { return s.head } - dah := share.EmptyRoot() + dah := share.EmptyEDSRoots() height := s.Head().Height() + 1 rh := s.GenRawHeader(height, s.Head().Hash(), libhead.Hash(s.Head().Commit.Hash()), dah.Hash()) s.head = &header.ExtendedHeader{ @@ -199,7 +212,7 @@ func (s *TestSuite) Commit(h *header.RawHeader) *types.Commit { ValidatorIndex: int32(i), Height: h.Height, Round: round, - Timestamp: tmtime.Now().UTC(), + Timestamp: h.Time, Type: tmproto.PrecommitType, BlockID: bid, } @@ -229,7 +242,7 @@ func RandExtendedHeader(t testing.TB) *header.ExtendedHeader { } func RandExtendedHeaderAtTimestamp(t testing.TB, timestamp time.Time) *header.ExtendedHeader { - dah := share.EmptyRoot() + dah := share.EmptyEDSRoots() rh := RandRawHeader(t) rh.DataHash = dah.Hash() @@ -328,9 +341,9 @@ func ExtendedHeadersFromEdsses(t testing.TB, edsses []*rsmt2d.ExtendedDataSquare for i, eds := range edsses { gen := RandRawHeader(t) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - gen.DataHash = dah.Hash() + gen.DataHash = roots.Hash() gen.ValidatorsHash = valSet.Hash() gen.NextValidatorsHash = valSet.Hash() gen.Height = int64(i + 1) @@ -347,7 +360,7 @@ func ExtendedHeadersFromEdsses(t testing.TB, edsses []*rsmt2d.ExtendedDataSquare RawHeader: *gen, Commit: commit, ValidatorSet: valSet, - DAH: dah, + DAH: roots, } require.NoError(t, eh.Validate()) headers[i] = eh @@ -358,7 +371,7 @@ func ExtendedHeadersFromEdsses(t testing.TB, edsses []*rsmt2d.ExtendedDataSquare func ExtendedHeaderFromEDS(t testing.TB, height uint64, eds *rsmt2d.ExtendedDataSquare) *header.ExtendedHeader { valSet, vals := RandValidatorSet(10, 10) gen := RandRawHeader(t) - dah, err := share.NewRoot(eds) + dah, err := share.NewAxisRoots(eds) require.NoError(t, err) gen.DataHash = dah.Hash() diff --git a/header/headertest/validate_test.go b/header/headertest/validate_test.go index d7c6e4b1cc..f569f0d6e0 100644 --- a/header/headertest/validate_test.go +++ b/header/headertest/validate_test.go @@ -10,7 +10,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/da" "github.com/celestiaorg/celestia-node/header" ) @@ -30,7 +30,11 @@ func TestValidate(t *testing.T) { }, { extendedHeader: getExtendedHeader(t, 3), - wantErr: "has version 3, this node supports up to version 2. " + + wantErr: "", + }, + { + extendedHeader: getExtendedHeader(t, 4), + wantErr: "has version 4, this node supports up to version 3. " + "Please upgrade to support new version. Note, 0 is not a valid version", }, } diff --git a/header/pb/extended_header.pb.go b/header/pb/extended_header.pb.go index 03ca6cba06..b1c989532a 100644 --- a/header/pb/extended_header.pb.go +++ b/header/pb/extended_header.pb.go @@ -5,7 +5,7 @@ package header_pb import ( fmt "fmt" - da "github.com/celestiaorg/celestia-app/v2/proto/celestia/core/v1/da" + da "github.com/celestiaorg/celestia-app/v3/proto/celestia/core/v1/da" proto "github.com/gogo/protobuf/proto" types "github.com/tendermint/tendermint/proto/tendermint/types" io "io" diff --git a/header/pb/extended_header.proto b/header/pb/extended_header.proto index aafb575327..e93b64d862 100644 --- a/header/pb/extended_header.proto +++ b/header/pb/extended_header.proto @@ -4,13 +4,13 @@ package header.pb; import "tendermint/types/types.proto"; import "tendermint/types/validator.proto"; -import "celestia/da/data_availability_header.proto"; +import "celestia/core/v1/da/data_availability_header.proto"; message ExtendedHeader { tendermint.types.Header header = 1; tendermint.types.Commit commit = 2; tendermint.types.ValidatorSet validator_set = 3; - celestia.da.DataAvailabilityHeader dah = 4; + celestia.core.v1.da.DataAvailabilityHeader dah = 4; } // Generated with: diff --git a/header/serde.go b/header/serde.go index fae310457b..689967bb8b 100644 --- a/header/serde.go +++ b/header/serde.go @@ -5,7 +5,7 @@ import ( core "github.com/tendermint/tendermint/types" "golang.org/x/crypto/blake2b" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/da" header_pb "github.com/celestiaorg/celestia-node/header/pb" ) @@ -64,37 +64,6 @@ func UnmarshalExtendedHeader(data []byte) (*ExtendedHeader, error) { return out, nil } -func ExtendedHeaderToProto(eh *ExtendedHeader) (*header_pb.ExtendedHeader, error) { - pb := &header_pb.ExtendedHeader{ - Header: eh.RawHeader.ToProto(), - Commit: eh.Commit.ToProto(), - } - valSet, err := eh.ValidatorSet.ToProto() - if err != nil { - return nil, err - } - pb.ValidatorSet = valSet - dah, err := eh.DAH.ToProto() - if err != nil { - return nil, err - } - pb.Dah = dah - return pb, nil -} - -func ProtoToExtendedHeader(pb *header_pb.ExtendedHeader) (*ExtendedHeader, error) { - bin, err := pb.Marshal() - if err != nil { - return nil, err - } - header := new(ExtendedHeader) - err = header.UnmarshalBinary(bin) - if err != nil { - return nil, err - } - return header, nil -} - // msgID computes an id for a pubsub message // TODO(@Wondertan): This cause additional allocations per each recvd message in the topic // diff --git a/libs/authtoken/authtoken.go b/libs/authtoken/authtoken.go index 873f58b6a3..2e28b745da 100644 --- a/libs/authtoken/authtoken.go +++ b/libs/authtoken/authtoken.go @@ -1,7 +1,10 @@ package authtoken import ( + "crypto/rand" "encoding/json" + "fmt" + "time" "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" @@ -17,17 +20,32 @@ func ExtractSignedPermissions(verifier jwt.Verifier, token string) ([]auth.Permi return nil, err } p := new(perms.JWTPayload) - err = json.Unmarshal(tk.Claims(), p) - if err != nil { + + if err := json.Unmarshal(tk.Claims(), p); err != nil { return nil, err } + if !p.ExpiresAt.IsZero() && p.ExpiresAt.Before(time.Now().UTC()) { + return nil, fmt.Errorf("token expired %s ago", time.Since(p.ExpiresAt)) + } return p.Allow, nil } // NewSignedJWT returns a signed JWT token with the passed permissions and signer. -func NewSignedJWT(signer jwt.Signer, permissions []auth.Permission) (string, error) { +func NewSignedJWT(signer jwt.Signer, permissions []auth.Permission, ttl time.Duration) (string, error) { + nonce := make([]byte, 32) + if _, err := rand.Read(nonce); err != nil { + return "", err + } + + var expiresAt time.Time + if ttl != 0 { + expiresAt = time.Now().UTC().Add(ttl) + } + token, err := jwt.NewBuilder(signer).Build(&perms.JWTPayload{ - Allow: permissions, + Allow: permissions, + Nonce: nonce, + ExpiresAt: expiresAt, }) if err != nil { return "", err diff --git a/libs/edssser/edssser.go b/libs/edssser/edssser.go index 10c9c6b3bd..dfb01e1756 100644 --- a/libs/edssser/edssser.go +++ b/libs/edssser/edssser.go @@ -9,17 +9,15 @@ import ( "testing" "time" - "github.com/ipfs/go-datastore" - - "github.com/celestiaorg/celestia-app/v2/pkg/da" - - "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/store" ) type Config struct { EDSSize int EDSWrites int + WriteFrom int EnableLog bool LogFilePath string StatLogFreq int @@ -29,25 +27,23 @@ type Config struct { // EDSsser stand for EDS Store Stresser. type EDSsser struct { config Config - datastore datastore.Batching edsstoreMu sync.Mutex - edsstore *eds.Store + edsstore *store.Store statsFileMu sync.Mutex statsFile *os.File } -func NewEDSsser(path string, datastore datastore.Batching, cfg Config) (*EDSsser, error) { - storeCfg := eds.DefaultParameters() - edsstore, err := eds.NewStore(storeCfg, path, datastore) +func NewEDSsser(path string, cfg Config) (*EDSsser, error) { + storeCfg := store.DefaultParameters() + edsstore, err := store.NewStore(storeCfg, path) if err != nil { return nil, err } return &EDSsser{ - config: cfg, - datastore: datastore, - edsstore: edsstore, + config: cfg, + edsstore: edsstore, }, nil } @@ -55,23 +51,14 @@ func (ss *EDSsser) Run(ctx context.Context) (stats Stats, err error) { ss.edsstoreMu.Lock() defer ss.edsstoreMu.Unlock() - err = ss.edsstore.Start(ctx) - if err != nil { - return stats, err - } defer func() { err = errors.Join(err, ss.edsstore.Stop(ctx)) }() - edsHashes, err := ss.edsstore.List() - if err != nil { - return stats, err - } - fmt.Printf("recovered %d EDSes\n\n", len(edsHashes)) - t := &testing.T{} - for toWrite := ss.config.EDSWrites - len(edsHashes); ctx.Err() == nil && toWrite > 0; toWrite-- { - took, err := ss.put(ctx, t) + writeTo := ss.config.WriteFrom + ss.config.EDSWrites + for height := ss.config.WriteFrom; ctx.Err() == nil && height < writeTo; height++ { + took, err := ss.put(ctx, t, height) stats.TotalWritten++ stats.TotalTime += took @@ -153,7 +140,7 @@ AvgTime %s ) } -func (ss *EDSsser) put(ctx context.Context, t *testing.T) (time.Duration, error) { +func (ss *EDSsser) put(ctx context.Context, t *testing.T, height int) (time.Duration, error) { ctx, cancel := context.WithTimeout(ctx, ss.config.OpTimeout) if ss.config.OpTimeout == 0 { ctx, cancel = context.WithCancel(ctx) @@ -162,12 +149,12 @@ func (ss *EDSsser) put(ctx context.Context, t *testing.T) (time.Duration, error) // divide by 2 to get ODS size as expected by RandEDS square := edstest.RandEDS(t, ss.config.EDSSize/2) - dah, err := da.NewDataAvailabilityHeader(square) + roots, err := share.NewAxisRoots(square) if err != nil { return 0, err } now := time.Now() - err = ss.edsstore.Put(ctx, dah.Hash(), square) + err = ss.edsstore.PutODSQ4(ctx, roots, uint64(height), square) return time.Since(now), err } diff --git a/libs/keystore/map_keystore.go b/libs/keystore/map_keystore.go index 9efd21f88b..b6355ed85c 100644 --- a/libs/keystore/map_keystore.go +++ b/libs/keystore/map_keystore.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" ) // mapKeystore is a simple in-memory Keystore implementation. diff --git a/libs/utils/close.go b/libs/utils/close.go new file mode 100644 index 0000000000..c9a2ddaa30 --- /dev/null +++ b/libs/utils/close.go @@ -0,0 +1,15 @@ +package utils + +import ( + "io" + + logging "github.com/ipfs/go-log/v2" +) + +// CloseAndLog closes the closer and logs any error that occurs. The function is handy wrapping +// to group closing and logging in one call for defer statements. +func CloseAndLog(log logging.StandardLogger, name string, closer io.Closer) { + if err := closer.Close(); err != nil { + log.Warnf("closing %s: %s", name, err) + } +} diff --git a/share/getters/utils.go b/libs/utils/ctx.go similarity index 53% rename from share/getters/utils.go rename to libs/utils/ctx.go index 2260183b4f..5a13232a16 100644 --- a/share/getters/utils.go +++ b/libs/utils/ctx.go @@ -1,24 +1,22 @@ -package getters +package utils import ( "context" - "errors" "time" - - logging "github.com/ipfs/go-log/v2" - "go.opentelemetry.io/otel" ) -var ( - tracer = otel.Tracer("share/getters") - log = logging.Logger("share/getters") +// ResetContextOnError returns a fresh context if the given context has an error. +func ResetContextOnError(ctx context.Context) context.Context { + if ctx.Err() != nil { + ctx = context.Background() + } - errOperationNotSupported = errors.New("operation is not supported") -) + return ctx +} -// ctxWithSplitTimeout will split timeout stored in context by splitFactor and return the result if +// CtxWithSplitTimeout will split timeout stored in context by splitFactor and return the result if // it is greater than minTimeout. minTimeout == 0 will be ignored, splitFactor <= 0 will be ignored -func ctxWithSplitTimeout( +func CtxWithSplitTimeout( ctx context.Context, splitFactor int, minTimeout time.Duration, @@ -42,16 +40,3 @@ func ctxWithSplitTimeout( } return context.WithTimeout(ctx, splitTimeout) } - -// ErrorContains reports whether any error in err's tree matches any error in targets tree. -func ErrorContains(err, target error) bool { - if errors.Is(err, target) || target == nil { - return true - } - - target = errors.Unwrap(target) - if target == nil { - return false - } - return ErrorContains(err, target) -} diff --git a/share/getters/utils_test.go b/libs/utils/ctx_test.go similarity index 62% rename from share/getters/utils_test.go rename to libs/utils/ctx_test.go index ce94d3ac04..c1fccfa48a 100644 --- a/share/getters/utils_test.go +++ b/libs/utils/ctx_test.go @@ -1,119 +1,13 @@ -package getters +package utils import ( "context" - "errors" - "fmt" "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func Test_ErrorContains(t *testing.T) { - err1 := errors.New("1") - err2 := errors.New("2") - - w1 := func(err error) error { - return fmt.Errorf("wrap1: %w", err) - } - w2 := func(err error) error { - return fmt.Errorf("wrap1: %w", err) - } - - type args struct { - err error - target error - } - tests := []struct { - name string - args args - want bool - }{ - { - "nil err", - args{ - err: nil, - target: err1, - }, - false, - }, - { - "nil target", - args{ - err: err1, - target: nil, - }, - true, - }, - { - "errors.Is true", - args{ - err: w1(err1), - target: err1, - }, - true, - }, - { - "errors.Is false", - args{ - err: w1(err1), - target: err2, - }, - false, - }, - { - "same wrap but different base error", - args{ - err: w1(err1), - target: w1(err2), - }, - false, - }, - { - "both wrapped true", - args{ - err: w1(err1), - target: w2(err1), - }, - true, - }, - { - "both wrapped false", - args{ - err: w1(err1), - target: w2(err2), - }, - false, - }, - { - "multierr first in slice", - args{ - err: errors.Join(w1(err1), w2(err2)), - target: w2(err1), - }, - true, - }, - { - "multierr second in slice", - args{ - err: errors.Join(w1(err1), w2(err2)), - target: w1(err2), - }, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equalf(t, - tt.want, - ErrorContains(tt.args.err, tt.args.target), - "ErrorContains(%v, %v)", tt.args.err, tt.args.target) - }) - } -} - func Test_ctxWithSplitTimeout(t *testing.T) { type args struct { ctxTimeout time.Duration @@ -216,7 +110,7 @@ func Test_ctxWithSplitTimeout(t *testing.T) { ctx, cancel = context.WithTimeout(ctx, tt.args.ctxTimeout) } t.Cleanup(cancel) - got, _ := ctxWithSplitTimeout(ctx, sf, tt.args.minTimeout) + got, _ := CtxWithSplitTimeout(ctx, sf, tt.args.minTimeout) dl, ok := got.Deadline() // in case no deadline is found in ctx or not expected to be found, check both cases apply at the // same time diff --git a/libs/utils/resetctx.go b/libs/utils/resetctx.go deleted file mode 100644 index a108cc27b4..0000000000 --- a/libs/utils/resetctx.go +++ /dev/null @@ -1,14 +0,0 @@ -package utils - -import ( - "context" -) - -// ResetContextOnError returns a fresh context if the given context has an error. -func ResetContextOnError(ctx context.Context) context.Context { - if ctx.Err() != nil { - ctx = context.Background() - } - - return ctx -} diff --git a/libs/utils/sessions.go b/libs/utils/sessions.go new file mode 100644 index 0000000000..9e0cf85905 --- /dev/null +++ b/libs/utils/sessions.go @@ -0,0 +1,45 @@ +package utils + +import ( + "context" + "sync" +) + +// Sessions manages concurrent sessions for the specified key. +// It ensures only one session can proceed for each key, avoiding duplicate efforts. +// If a session is already active for the given key, it waits until the session completes or +// context error occurs. +type Sessions struct { + active sync.Map +} + +func NewSessions() *Sessions { + return &Sessions{} +} + +// StartSession attempts to start a new session for the given key. It provides a release function +// to clean up the session lock for this key, once the session is complete. +func (s *Sessions) StartSession( + ctx context.Context, + key any, +) (endSession func(), err error) { + // Attempt to load or initialize a channel to track the sampling session for this height + lockChan, alreadyActive := s.active.LoadOrStore(key, make(chan struct{})) + if alreadyActive { + // If a session is already active, wait for it to complete + select { + case <-lockChan.(chan struct{}): + case <-ctx.Done(): + return func() {}, ctx.Err() + } + // previous session has completed, try to obtain the lock for this session + return s.StartSession(ctx, key) + } + + // Provide a function to release the lock once session is complete + releaseLock := func() { + close(lockChan.(chan struct{})) + s.active.Delete(key) + } + return releaseLock, nil +} diff --git a/libs/utils/sessions_test.go b/libs/utils/sessions_test.go new file mode 100644 index 0000000000..5128c2f9f8 --- /dev/null +++ b/libs/utils/sessions_test.go @@ -0,0 +1,127 @@ +package utils + +import ( + "context" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// TestSessionsSerialExecution verifies that multiple sessions for the same key are executed +// sequentially. +func TestSessionsSerialExecution(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + t.Cleanup(cancel) + + sessions := NewSessions() + key := "testKey" + activeCount := atomic.Int32{} + var wg sync.WaitGroup + + numSessions := 20 + + for i := 0; i < numSessions; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + endSession, err := sessions.StartSession(ctx, key) + require.NoError(t, err) + old := activeCount.Add(1) + require.Equal(t, int32(1), old) + // Simulate some work + time.Sleep(50 * time.Millisecond) + old = activeCount.Add(-1) + require.Equal(t, int32(0), old) + // Release the session + endSession() + }(i) + } + + wg.Wait() +} + +func TestSessionsContextCancellation(t *testing.T) { + sessions := NewSessions() + key := "testCancelKey" + + // Start the first session which will hold the lock for a while + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + release, err := sessions.StartSession(ctx, key) + if err != nil { + t.Errorf("First session: failed to start: %v", err) + return + } + + // Hold the session for 1 second + time.Sleep(1 * time.Second) + release() + }() + + // Give the first goroutine a moment to acquire the session + time.Sleep(100 * time.Millisecond) + + // Attempt to start a second session with a context that times out before the first session releases + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + t.Cleanup(cancel) + + _, err := sessions.StartSession(ctx, key) + require.ErrorIs(t, err, context.DeadlineExceeded) + + // Attempt to start a second session with a context that is canceled before the first session + // releases + ctx, cancel = context.WithCancel(context.Background()) + cancel() + + _, err = sessions.StartSession(ctx, key) + require.ErrorIs(t, err, context.Canceled) + + wg.Wait() +} + +// TestSessions_ConcurrentDifferentKeys ensures that sessions with different keys run concurrently. +func TestSessions_ConcurrentDifferentKeys(t *testing.T) { + sessions := NewSessions() + numKeys := 20 + var wg sync.WaitGroup + startCh := make(chan struct{}) + activeSessions := atomic.Int32{} + maxActive := int32(0) + + for i := 0; i < numKeys; i++ { + wg.Add(1) + go func(key int) { + defer wg.Done() + ctx := context.Background() + endSession, err := sessions.StartSession(ctx, key) + require.NoError(t, err) + + active := activeSessions.Add(1) + if active > maxActive { + maxActive = active + } + + // Wait to simulate work + time.Sleep(100 * time.Millisecond) + + activeSessions.Add(-1) + endSession() + }(i) + } + + // Start all goroutines + close(startCh) + wg.Wait() + + if maxActive > int32(numKeys) { + t.Errorf("Expected %d concurrent active sessions, but got %d", numKeys, maxActive) + } +} diff --git a/logs/logs.go b/logs/logs.go index 9c8429c914..a93943bf06 100644 --- a/logs/logs.go +++ b/logs/logs.go @@ -6,9 +6,6 @@ import ( func SetAllLoggers(level logging.LogLevel) { logging.SetAllLoggers(level) - _ = logging.SetLogLevel("engine", "FATAL") - _ = logging.SetLogLevel("blockservice", "WARN") - _ = logging.SetLogLevel("bs:sess", "WARN") _ = logging.SetLogLevel("addrutil", "INFO") _ = logging.SetLogLevel("dht", "ERROR") _ = logging.SetLogLevel("swarm2", "WARN") @@ -19,10 +16,10 @@ func SetAllLoggers(level logging.LogLevel) { _ = logging.SetLogLevel("bitswap/client/provqrymgr", "WARN") _ = logging.SetLogLevel("bitswap/client/getter", "WARN") _ = logging.SetLogLevel("bitswap/client/sesspeermgr", "WARN") - _ = logging.SetLogLevel("bitswap/network", "ERROR") + _ = logging.SetLogLevel("bitswap/network", "WARN") _ = logging.SetLogLevel("bitswap/session", "WARN") _ = logging.SetLogLevel("bitswap/server", "WARN") - _ = logging.SetLogLevel("bitswap/server/decision", "WARN") + _ = logging.SetLogLevel("bitswap/server/decision", "INFO") _ = logging.SetLogLevel("connmgr", "WARN") _ = logging.SetLogLevel("nat", "INFO") _ = logging.SetLogLevel("dht/RtRefreshManager", "FATAL") @@ -32,8 +29,6 @@ func SetAllLoggers(level logging.LogLevel) { _ = logging.SetLogLevel("net/identify", "ERROR") _ = logging.SetLogLevel("shrex/nd", "WARN") _ = logging.SetLogLevel("shrex/eds", "WARN") - _ = logging.SetLogLevel("dagstore", "WARN") - _ = logging.SetLogLevel("dagstore/upgrader", "WARN") _ = logging.SetLogLevel("fx", "FATAL") } diff --git a/nodebuilder/blob/blob.go b/nodebuilder/blob/blob.go index b93257e8d9..b8069f82b5 100644 --- a/nodebuilder/blob/blob.go +++ b/nodebuilder/blob/blob.go @@ -3,8 +3,9 @@ package blob import ( "context" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/blob" - "github.com/celestiaorg/celestia-node/share" ) var _ Module = (*API)(nil) @@ -18,7 +19,7 @@ type Module interface { // Uses default wallet registered on the Node. Submit(_ context.Context, _ []*blob.Blob, _ *blob.SubmitOptions) (height uint64, _ error) // Get retrieves the blob by commitment under the given namespace and height. - Get(_ context.Context, height uint64, _ share.Namespace, _ blob.Commitment) (*blob.Blob, error) + Get(_ context.Context, height uint64, _ libshare.Namespace, _ blob.Commitment) (*blob.Blob, error) // GetAll returns all blobs under the given namespaces at the given height. // If all blobs were found without any errors, the user will receive a list of blobs. // If the BlobService couldn't find any blobs under the requested namespaces, @@ -28,21 +29,21 @@ type Module interface { // the user will receive all found blobs along with a combined error message. // // All blobs will preserve the order of the namespaces that were requested. - GetAll(_ context.Context, height uint64, _ []share.Namespace) ([]*blob.Blob, error) + GetAll(_ context.Context, height uint64, _ []libshare.Namespace) ([]*blob.Blob, error) // GetProof retrieves proofs in the given namespaces at the given height by commitment. - GetProof(_ context.Context, height uint64, _ share.Namespace, _ blob.Commitment) (*blob.Proof, error) + GetProof(_ context.Context, height uint64, _ libshare.Namespace, _ blob.Commitment) (*blob.Proof, error) // Included checks whether a blob's given commitment(Merkle subtree root) is included at // given height and under the namespace. - Included(_ context.Context, height uint64, _ share.Namespace, _ *blob.Proof, _ blob.Commitment) (bool, error) + Included(_ context.Context, height uint64, _ libshare.Namespace, _ *blob.Proof, _ blob.Commitment) (bool, error) // GetCommitmentProof generates a commitment proof for a share commitment. GetCommitmentProof( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, shareCommitment []byte, ) (*blob.CommitmentProof, error) // Subscribe to published blobs from the given namespace as they are included. - Subscribe(_ context.Context, _ share.Namespace) (<-chan *blob.SubscriptionResponse, error) + Subscribe(_ context.Context, _ libshare.Namespace) (<-chan *blob.SubscriptionResponse, error) } type API struct { @@ -55,36 +56,36 @@ type API struct { Get func( context.Context, uint64, - share.Namespace, + libshare.Namespace, blob.Commitment, ) (*blob.Blob, error) `perm:"read"` GetAll func( context.Context, uint64, - []share.Namespace, + []libshare.Namespace, ) ([]*blob.Blob, error) `perm:"read"` GetProof func( context.Context, uint64, - share.Namespace, + libshare.Namespace, blob.Commitment, ) (*blob.Proof, error) `perm:"read"` Included func( context.Context, uint64, - share.Namespace, + libshare.Namespace, *blob.Proof, blob.Commitment, ) (bool, error) `perm:"read"` GetCommitmentProof func( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, shareCommitment []byte, ) (*blob.CommitmentProof, error) `perm:"read"` Subscribe func( context.Context, - share.Namespace, + libshare.Namespace, ) (<-chan *blob.SubscriptionResponse, error) `perm:"read"` } } @@ -96,20 +97,20 @@ func (api *API) Submit(ctx context.Context, blobs []*blob.Blob, options *blob.Su func (api *API) Get( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, commitment blob.Commitment, ) (*blob.Blob, error) { return api.Internal.Get(ctx, height, namespace, commitment) } -func (api *API) GetAll(ctx context.Context, height uint64, namespaces []share.Namespace) ([]*blob.Blob, error) { +func (api *API) GetAll(ctx context.Context, height uint64, namespaces []libshare.Namespace) ([]*blob.Blob, error) { return api.Internal.GetAll(ctx, height, namespaces) } func (api *API) GetProof( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, commitment blob.Commitment, ) (*blob.Proof, error) { return api.Internal.GetProof(ctx, height, namespace, commitment) @@ -118,7 +119,7 @@ func (api *API) GetProof( func (api *API) GetCommitmentProof( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, shareCommitment []byte, ) (*blob.CommitmentProof, error) { return api.Internal.GetCommitmentProof(ctx, height, namespace, shareCommitment) @@ -127,7 +128,7 @@ func (api *API) GetCommitmentProof( func (api *API) Included( ctx context.Context, height uint64, - namespace share.Namespace, + namespace libshare.Namespace, proof *blob.Proof, commitment blob.Commitment, ) (bool, error) { @@ -136,7 +137,7 @@ func (api *API) Included( func (api *API) Subscribe( ctx context.Context, - namespace share.Namespace, + namespace libshare.Namespace, ) (<-chan *blob.SubscriptionResponse, error) { return api.Internal.Subscribe(ctx, namespace) } diff --git a/nodebuilder/blob/cmd/blob.go b/nodebuilder/blob/cmd/blob.go index 5e57f91e0d..17eec5bbf1 100644 --- a/nodebuilder/blob/cmd/blob.go +++ b/nodebuilder/blob/cmd/blob.go @@ -11,10 +11,11 @@ import ( "github.com/spf13/cobra" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/blob" cmdnode "github.com/celestiaorg/celestia-node/cmd" state "github.com/celestiaorg/celestia-node/nodebuilder/state/cmd" - "github.com/celestiaorg/celestia-node/share" ) // flagFileInput allows the user to provide file path to the json file @@ -42,11 +43,11 @@ var getCmd = &cobra.Command{ Short: "Returns the blob for the given namespace by commitment at a particular height.\n" + "Note:\n* Both namespace and commitment input parameters are expected to be in their hex representation.", PreRunE: func(_ *cobra.Command, args []string) error { - if !strings.HasPrefix(args[1], "0x") { - return fmt.Errorf("only hex namespace is supported") + if !strings.HasPrefix(args[0], "0x") { + args[0] = "0x" + args[0] } - if !strings.HasPrefix(args[2], "0x") { - return fmt.Errorf("only hex commitment is supported") + if !strings.HasPrefix(args[1], "0x") { + args[1] = "0x" + args[1] } return nil }, @@ -83,8 +84,11 @@ var getAllCmd = &cobra.Command{ Short: "Returns all blobs for the given namespace at a particular height.\n" + "Note:\n* Namespace input parameter is expected to be in its hex representation.", PreRunE: func(_ *cobra.Command, args []string) error { + if !strings.HasPrefix(args[0], "0x") { + args[0] = "0x" + args[0] + } if !strings.HasPrefix(args[1], "0x") { - return fmt.Errorf("only hex namespace is supported") + args[1] = "0x" + args[1] } return nil }, @@ -105,7 +109,7 @@ var getAllCmd = &cobra.Command{ return fmt.Errorf("error parsing a namespace: %w", err) } - blobs, err := client.Blob.GetAll(cmd.Context(), height, []share.Namespace{namespace}) + blobs, err := client.Blob.GetAll(cmd.Context(), height, []libshare.Namespace{namespace}) return cmdnode.PrintOutput(blobs, err, formatData(args[1])) }, } @@ -135,7 +139,10 @@ var submitCmd = &cobra.Command{ }, PreRunE: func(_ *cobra.Command, args []string) error { if !strings.HasPrefix(args[0], "0x") { - return fmt.Errorf("only hex namespace is supported") + args[0] = "0x" + args[0] + } + if !strings.HasPrefix(args[1], "0x") { + args[1] = "0x" + args[1] } return nil }, @@ -234,11 +241,11 @@ var getProofCmd = &cobra.Command{ Short: "Retrieves the blob in the given namespaces at the given height by commitment and returns its Proof.\n" + "Note:\n* Both namespace and commitment input parameters are expected to be in their hex representation.", PreRunE: func(_ *cobra.Command, args []string) error { - if !strings.HasPrefix(args[1], "0x") { - return fmt.Errorf("only hex namespace is supported") + if !strings.HasPrefix(args[0], "0x") { + args[0] = "0x" + args[0] } - if !strings.HasPrefix(args[2], "0x") { - return fmt.Errorf("only hex commitment is supported") + if !strings.HasPrefix(args[1], "0x") { + args[1] = "0x" + args[1] } return nil }, @@ -285,8 +292,8 @@ func formatData(ns string) func(interface{}) interface{} { for i, b := range blobs { result[i] = tempBlob{ Namespace: ns, - Data: string(b.Data), - ShareVersion: b.ShareVersion, + Data: string(b.Data()), + ShareVersion: uint32(b.ShareVersion()), Commitment: "0x" + hex.EncodeToString(b.Commitment), Index: b.Index(), } @@ -297,8 +304,8 @@ func formatData(ns string) func(interface{}) interface{} { b := data.(*blob.Blob) return tempBlob{ Namespace: ns, - Data: string(b.Data), - ShareVersion: b.ShareVersion, + Data: string(b.Data()), + ShareVersion: uint32(b.ShareVersion()), Commitment: "0x" + hex.EncodeToString(b.Commitment), Index: b.Index(), } diff --git a/nodebuilder/blob/mocks/api.go b/nodebuilder/blob/mocks/api.go index 39815179bd..a7c1d7d909 100644 --- a/nodebuilder/blob/mocks/api.go +++ b/nodebuilder/blob/mocks/api.go @@ -9,8 +9,8 @@ import ( reflect "reflect" blob "github.com/celestiaorg/celestia-node/blob" - share "github.com/celestiaorg/celestia-node/share" state "github.com/celestiaorg/celestia-node/state" + share "github.com/celestiaorg/go-square/v2/share" gomock "github.com/golang/mock/gomock" ) @@ -126,3 +126,18 @@ func (mr *MockModuleMockRecorder) Submit(arg0, arg1, arg2 interface{}) *gomock.C mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Submit", reflect.TypeOf((*MockModule)(nil).Submit), arg0, arg1, arg2) } + +// Subscribe mocks base method. +func (m *MockModule) Subscribe(arg0 context.Context, arg1 share.Namespace) (<-chan *blob.SubscriptionResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Subscribe", arg0, arg1) + ret0, _ := ret[0].(<-chan *blob.SubscriptionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Subscribe indicates an expected call of Subscribe. +func (mr *MockModuleMockRecorder) Subscribe(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockModule)(nil).Subscribe), arg0, arg1) +} diff --git a/nodebuilder/blob/module.go b/nodebuilder/blob/module.go index 3ff6b9892b..cf07ed3732 100644 --- a/nodebuilder/blob/module.go +++ b/nodebuilder/blob/module.go @@ -9,7 +9,7 @@ import ( "github.com/celestiaorg/celestia-node/header" headerService "github.com/celestiaorg/celestia-node/nodebuilder/header" "github.com/celestiaorg/celestia-node/nodebuilder/state" - "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" ) func ConstructModule() fx.Option { @@ -27,7 +27,7 @@ func ConstructModule() fx.Option { fx.Provide(fx.Annotate( func( state state.Module, - sGetter share.Getter, + sGetter shwap.Getter, getByHeightFn func(context.Context, uint64) (*header.ExtendedHeader, error), subscribeFn func(context.Context) (<-chan *header.ExtendedHeader, error), ) *blob.Service { diff --git a/nodebuilder/core/module.go b/nodebuilder/core/module.go index 0b0c409406..441907ce32 100644 --- a/nodebuilder/core/module.go +++ b/nodebuilder/core/module.go @@ -12,8 +12,8 @@ import ( "github.com/celestiaorg/celestia-node/libs/fxutil" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" + "github.com/celestiaorg/celestia-node/store" ) // ConstructModule collects all the components and services related to managing the relationship @@ -38,7 +38,7 @@ func ConstructModule(tp node.Type, cfg *Config, options ...fx.Option) fx.Option fxutil.ProvideAs( func( fetcher *core.BlockFetcher, - store *eds.Store, + store *store.Store, construct header.ConstructFn, opts []core.Option, ) (*core.Exchange, error) { @@ -55,7 +55,7 @@ func ConstructModule(tp node.Type, cfg *Config, options ...fx.Option) fx.Option fetcher *core.BlockFetcher, pubsub *shrexsub.PubSub, construct header.ConstructFn, - store *eds.Store, + store *store.Store, chainID p2p.Network, opts []core.Option, ) (*core.Listener, error) { diff --git a/nodebuilder/da/da.go b/nodebuilder/da/da.go index 0d604d769f..1e59aabce8 100644 --- a/nodebuilder/da/da.go +++ b/nodebuilder/da/da.go @@ -12,16 +12,16 @@ type Module interface { } // API is a wrapper around Module for the RPC. -// TODO(@distractedm1nd): These structs need to be autogenerated. type API struct { Internal struct { - MaxBlobSize func(ctx context.Context) (uint64, error) `perm:"read"` - Get func(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Blob, error) `perm:"read"` - GetIDs func(ctx context.Context, height uint64, ns da.Namespace) ([]da.ID, error) `perm:"read"` - GetProofs func(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Proof, error) `perm:"read"` - Commit func(ctx context.Context, blobs []da.Blob, ns da.Namespace) ([]da.Commitment, error) `perm:"read"` - Validate func(context.Context, []da.ID, []da.Proof, da.Namespace) ([]bool, error) `perm:"read"` - Submit func(context.Context, []da.Blob, float64, da.Namespace) ([]da.ID, error) `perm:"write"` + MaxBlobSize func(ctx context.Context) (uint64, error) `perm:"read"` + Get func(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Blob, error) `perm:"read"` + GetIDs func(ctx context.Context, height uint64, ns da.Namespace) (*da.GetIDsResult, error) `perm:"read"` + GetProofs func(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Proof, error) `perm:"read"` + Commit func(ctx context.Context, blobs []da.Blob, ns da.Namespace) ([]da.Commitment, error) `perm:"read"` + Validate func(context.Context, []da.ID, []da.Proof, da.Namespace) ([]bool, error) `perm:"read"` + Submit func(context.Context, []da.Blob, float64, da.Namespace) ([]da.ID, error) `perm:"write"` + SubmitWithOptions func(context.Context, []da.Blob, float64, da.Namespace, []byte) ([]da.ID, error) `perm:"write"` } } @@ -33,7 +33,7 @@ func (api *API) Get(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Blo return api.Internal.Get(ctx, ids, ns) } -func (api *API) GetIDs(ctx context.Context, height uint64, ns da.Namespace) ([]da.ID, error) { +func (api *API) GetIDs(ctx context.Context, height uint64, ns da.Namespace) (*da.GetIDsResult, error) { return api.Internal.GetIDs(ctx, height, ns) } @@ -52,3 +52,13 @@ func (api *API) Validate(ctx context.Context, ids []da.ID, proofs []da.Proof, ns func (api *API) Submit(ctx context.Context, blobs []da.Blob, gasPrice float64, ns da.Namespace) ([]da.ID, error) { return api.Internal.Submit(ctx, blobs, gasPrice, ns) } + +func (api *API) SubmitWithOptions( + ctx context.Context, + blobs []da.Blob, + gasPrice float64, + ns da.Namespace, + options []byte, +) ([]da.ID, error) { + return api.Internal.SubmitWithOptions(ctx, blobs, gasPrice, ns, options) +} diff --git a/nodebuilder/da/mocks/api.go b/nodebuilder/da/mocks/api.go index 5895240906..53679a4933 100644 --- a/nodebuilder/da/mocks/api.go +++ b/nodebuilder/da/mocks/api.go @@ -9,6 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + da "github.com/rollkit/go-da" ) // MockModule is a mock of Module interface. @@ -65,10 +66,10 @@ func (mr *MockModuleMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call } // GetIDs mocks base method. -func (m *MockModule) GetIDs(arg0 context.Context, arg1 uint64, arg2 []byte) ([][]byte, error) { +func (m *MockModule) GetIDs(arg0 context.Context, arg1 uint64, arg2 []byte) (*da.GetIDsResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetIDs", arg0, arg1, arg2) - ret0, _ := ret[0].([][]byte) + ret0, _ := ret[0].(*da.GetIDsResult) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -124,6 +125,21 @@ func (mr *MockModuleMockRecorder) Submit(arg0, arg1, arg2, arg3 interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Submit", reflect.TypeOf((*MockModule)(nil).Submit), arg0, arg1, arg2, arg3) } +// SubmitWithOptions mocks base method. +func (m *MockModule) SubmitWithOptions(arg0 context.Context, arg1 [][]byte, arg2 float64, arg3, arg4 []byte) ([][]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubmitWithOptions", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].([][]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitWithOptions indicates an expected call of SubmitWithOptions. +func (mr *MockModuleMockRecorder) SubmitWithOptions(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitWithOptions", reflect.TypeOf((*MockModule)(nil).SubmitWithOptions), arg0, arg1, arg2, arg3, arg4) +} + // Validate mocks base method. func (m *MockModule) Validate(arg0 context.Context, arg1, arg2 [][]byte, arg3 []byte) ([]bool, error) { m.ctrl.T.Helper() diff --git a/nodebuilder/da/service.go b/nodebuilder/da/service.go index 113fe10c80..c7e403fbff 100644 --- a/nodebuilder/da/service.go +++ b/nodebuilder/da/service.go @@ -4,17 +4,17 @@ import ( "context" "encoding/binary" "encoding/json" - "fmt" "strings" logging "github.com/ipfs/go-log/v2" "github.com/rollkit/go-da" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/celestia-node/blob" + "github.com/celestiaorg/celestia-node/header" nodeblob "github.com/celestiaorg/celestia-node/nodebuilder/blob" - "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/state" ) @@ -28,12 +28,32 @@ var log = logging.Logger("go-da") const heightLen = 8 type Service struct { - blobServ nodeblob.Module + blobServ nodeblob.Module + headerGetter func(context.Context, uint64) (*header.ExtendedHeader, error) } -func NewService(blobMod nodeblob.Module) *Service { +// SubmitOptions defines options for blob submission using SubmitWithOptions. +// +// SubmitWithOptions expects JSON serialized version of this struct; all fields are optional. +type SubmitOptions struct { + // KeyName is the name of key from the keystore used to sign transactions. + KeyName string `json:"key_name,omitempty"` + + // SignerAddress is the address that signs the transaction. + // This address must be stored locally in the key store. + SignerAddress string `json:"signer_address,omitempty"` + + // FeeGranterAddress specifies the account that will pay for the transaction fee. + FeeGranterAddress string `json:"fee_granter_address,omitempty"` +} + +func NewService( + blobMod nodeblob.Module, + headerGetter func(context.Context, uint64) (*header.ExtendedHeader, error), +) *Service { return &Service{ - blobServ: blobMod, + blobServ: blobMod, + headerGetter: headerGetter, } } @@ -47,41 +67,58 @@ func (s *Service) Get(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.B blobs := make([]da.Blob, 0, len(ids)) for _, id := range ids { height, commitment := SplitID(id) - log.Debugw("getting blob", "height", height, "commitment", commitment, "namespace", share.Namespace(ns)) - currentBlob, err := s.blobServ.Get(ctx, height, ns, commitment) - log.Debugw("got blob", "height", height, "commitment", commitment, "namespace", share.Namespace(ns)) + namespace, err := libshare.NewNamespaceFromBytes(ns) + if err != nil { + return nil, err + } + log.Debugw("getting blob", "height", height, "commitment", commitment, "namespace", namespace) + currentBlob, err := s.blobServ.Get(ctx, height, namespace, commitment) + log.Debugw("got blob", "height", height, "commitment", commitment, "namespace", namespace) if err != nil { return nil, err } - blobs = append(blobs, currentBlob.Data) + blobs = append(blobs, currentBlob.Data()) } return blobs, nil } // GetIDs returns IDs of all Blobs located in DA at given height. -func (s *Service) GetIDs(ctx context.Context, height uint64, namespace da.Namespace) ([]da.ID, error) { +func (s *Service) GetIDs(ctx context.Context, height uint64, namespace da.Namespace) (*da.GetIDsResult, error) { + ns, err := libshare.NewNamespaceFromBytes(namespace) + if err != nil { + return nil, err + } + var ids []da.ID //nolint:prealloc - log.Debugw("getting ids", "height", height, "namespace", share.Namespace(namespace)) - blobs, err := s.blobServ.GetAll(ctx, height, []share.Namespace{namespace}) - log.Debugw("got ids", "height", height, "namespace", share.Namespace(namespace)) + log.Debugw("getting ids", "height", height, "namespace", ns) + blobs, err := s.blobServ.GetAll(ctx, height, []libshare.Namespace{ns}) + log.Debugw("got ids", "height", height, "namespace", ns) if err != nil { if strings.Contains(err.Error(), blob.ErrBlobNotFound.Error()) { - return nil, nil + return nil, nil //nolint:nilnil } return nil, err } for _, b := range blobs { ids = append(ids, MakeID(height, b.Commitment)) } - return ids, nil + h, err := s.headerGetter(ctx, height) + if err != nil { + return nil, err + } + return &da.GetIDsResult{IDs: ids, Timestamp: h.Time()}, nil } // GetProofs returns inclusion Proofs for all Blobs located in DA at given height. func (s *Service) GetProofs(ctx context.Context, ids []da.ID, namespace da.Namespace) ([]da.Proof, error) { proofs := make([]da.Proof, len(ids)) for i, id := range ids { + ns, err := libshare.NewNamespaceFromBytes(namespace) + if err != nil { + return nil, err + } height, commitment := SplitID(id) - proof, err := s.blobServ.GetProof(ctx, height, namespace, commitment) + proof, err := s.blobServ.GetProof(ctx, height, ns, commitment) if err != nil { return nil, err } @@ -105,15 +142,31 @@ func (s *Service) Submit( daBlobs []da.Blob, gasPrice float64, namespace da.Namespace, +) ([]da.ID, error) { + return s.SubmitWithOptions(ctx, daBlobs, gasPrice, namespace, nil) +} + +// SubmitWithOptions submits the Blobs to Data Availability layer. +func (s *Service) SubmitWithOptions( + ctx context.Context, + daBlobs []da.Blob, + gasPrice float64, + namespace da.Namespace, + options []byte, ) ([]da.ID, error) { blobs, _, err := s.blobsAndCommitments(daBlobs, namespace) if err != nil { return nil, err } - opts := state.NewTxConfig(state.WithGasPrice(gasPrice)) + opts, err := parseOptions(options) + if err != nil { + return nil, err + } + opts = append(opts, state.WithGasPrice(gasPrice)) + cfg := state.NewTxConfig(opts...) - height, err := s.blobServ.Submit(ctx, blobs, opts) + height, err := s.blobServ.Submit(ctx, blobs, cfg) if err != nil { log.Error("failed to submit blobs", "height", height, "gas price", gasPrice) return nil, err @@ -126,6 +179,31 @@ func (s *Service) Submit( return ids, nil } +func parseOptions(options []byte) ([]state.ConfigOption, error) { + var opts []state.ConfigOption + + if len(options) == 0 { + return opts, nil + } + + parsedOpts := SubmitOptions{} + err := json.Unmarshal(options, &parsedOpts) + if err != nil { + return nil, err + } + + if parsedOpts.KeyName != "" { + opts = append(opts, state.WithKeyName(parsedOpts.KeyName)) + } + if parsedOpts.SignerAddress != "" { + opts = append(opts, state.WithSignerAddress(parsedOpts.SignerAddress)) + } + if parsedOpts.FeeGranterAddress != "" { + opts = append(opts, state.WithFeeGranterAddress(parsedOpts.FeeGranterAddress)) + } + return opts, nil +} + // blobsAndCommitments converts []da.Blob to []*blob.Blob and generates corresponding // []da.Commitment func (s *Service) blobsAndCommitments( @@ -134,7 +212,11 @@ func (s *Service) blobsAndCommitments( blobs := make([]*blob.Blob, 0, len(daBlobs)) commitments := make([]da.Commitment, 0, len(daBlobs)) for _, daBlob := range daBlobs { - b, err := blob.NewBlobV0(namespace, daBlob) + ns, err := libshare.NewNamespaceFromBytes(namespace) + if err != nil { + return nil, nil, err + } + b, err := blob.NewBlobV0(ns, daBlob) if err != nil { return nil, nil, err } @@ -164,13 +246,16 @@ func (s *Service) Validate( proofs[i] = blobProof } for i, id := range ids { + ns, err := libshare.NewNamespaceFromBytes(namespace) + if err != nil { + return nil, err + } height, commitment := SplitID(id) // TODO(tzdybal): for some reason, if proof doesn't match commitment, API returns (false, "blob: // invalid proof") but analysis of the code in celestia-node implies this should never happen - // maybe it's caused by openrpc? there is no way of gently handling errors here, but returned // value is fine for us - fmt.Println("proof", proofs[i] == nil, "commitment", commitment == nil) - isIncluded, _ := s.blobServ.Included(ctx, height, namespace, proofs[i], commitment) + isIncluded, _ := s.blobServ.Included(ctx, height, ns, proofs[i], commitment) included[i] = isIncluded } return included, nil diff --git a/nodebuilder/das/constructors.go b/nodebuilder/das/constructors.go index 37a90086a8..db35c1a0c2 100644 --- a/nodebuilder/das/constructors.go +++ b/nodebuilder/das/constructors.go @@ -12,10 +12,9 @@ import ( "github.com/celestiaorg/celestia-node/das" "github.com/celestiaorg/celestia-node/header" modfraud "github.com/celestiaorg/celestia-node/nodebuilder/fraud" - "github.com/celestiaorg/celestia-node/pruner" "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds/byzantine" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) var _ Module = (*daserStub)(nil) @@ -45,11 +44,8 @@ func newDASer( batching datastore.Batching, fraudServ fraud.Service[*header.ExtendedHeader], bFn shrexsub.BroadcastFn, - availWindow pruner.AvailabilityWindow, options ...das.Option, ) (*das.DASer, *modfraud.ServiceBreaker[*das.DASer, *header.ExtendedHeader], error) { - options = append(options, das.WithSamplingWindow(availWindow)) - ds, err := das.NewDASer(da, hsub, store, batching, fraudServ, bFn, options...) if err != nil { return nil, nil, err diff --git a/nodebuilder/das/das.go b/nodebuilder/das/das.go index f1dddbc4df..4d2b49dc56 100644 --- a/nodebuilder/das/das.go +++ b/nodebuilder/das/das.go @@ -17,7 +17,6 @@ type Module interface { } // API is a wrapper around Module for the RPC. -// TODO(@distractedm1nd): These structs need to be autogenerated. type API struct { Internal struct { SamplingStats func(ctx context.Context) (das.SamplingStats, error) `perm:"read"` diff --git a/nodebuilder/fraud/constructors.go b/nodebuilder/fraud/constructors.go index 037abb2e51..14593f3fc8 100644 --- a/nodebuilder/fraud/constructors.go +++ b/nodebuilder/fraud/constructors.go @@ -17,7 +17,7 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/p2p" ) -func fraudUnmarshaler() fraud.ProofUnmarshaler[*header.ExtendedHeader] { +func Unmarshaler() fraud.ProofUnmarshaler[*header.ExtendedHeader] { return defaultProofUnmarshaler } diff --git a/nodebuilder/fraud/fraud.go b/nodebuilder/fraud/fraud.go index 178b0527a1..5577f049d2 100644 --- a/nodebuilder/fraud/fraud.go +++ b/nodebuilder/fraud/fraud.go @@ -24,7 +24,6 @@ type Module interface { } // API is a wrapper around Module for the RPC. -// TODO(@distractedm1nd): These structs need to be autogenerated. type API struct { Internal struct { Subscribe func(context.Context, fraud.ProofType) (<-chan *Proof, error) `perm:"read"` diff --git a/nodebuilder/fraud/module.go b/nodebuilder/fraud/module.go index bf353f63c6..62903226ba 100644 --- a/nodebuilder/fraud/module.go +++ b/nodebuilder/fraud/module.go @@ -14,7 +14,7 @@ var log = logging.Logger("module/fraud") func ConstructModule(tp node.Type) fx.Option { baseComponent := fx.Options( - fx.Provide(fraudUnmarshaler), + fx.Provide(Unmarshaler), fx.Provide(func(serv fraud.Service[*header.ExtendedHeader]) fraud.Getter[*header.ExtendedHeader] { return serv }), diff --git a/nodebuilder/gateway/flags.go b/nodebuilder/gateway/flags.go index 6da4a66f03..62c6f2a6d2 100644 --- a/nodebuilder/gateway/flags.go +++ b/nodebuilder/gateway/flags.go @@ -20,7 +20,7 @@ func Flags() *flag.FlagSet { flags.Bool( enabledFlag, false, - "Enables the REST gateway", + "Enables the REST gateway. WARNING: gateway will be deprecated in upcoming release!", ) flags.String( addrFlag, diff --git a/nodebuilder/gateway/flags_test.go b/nodebuilder/gateway/flags_test.go index 5f55ac77f2..893d793fcc 100644 --- a/nodebuilder/gateway/flags_test.go +++ b/nodebuilder/gateway/flags_test.go @@ -16,7 +16,7 @@ func TestFlags(t *testing.T) { enabled := flags.Lookup(enabledFlag) require.NotNil(t, enabled) assert.Equal(t, "false", enabled.Value.String()) - assert.Equal(t, "Enables the REST gateway", enabled.Usage) + assert.Equal(t, "Enables the REST gateway. WARNING: gateway will be deprecated in upcoming release!", enabled.Usage) addr := flags.Lookup(addrFlag) require.NotNil(t, addr) diff --git a/nodebuilder/gateway/module.go b/nodebuilder/gateway/module.go index 4cdf325dc0..20306aa7de 100644 --- a/nodebuilder/gateway/module.go +++ b/nodebuilder/gateway/module.go @@ -22,6 +22,7 @@ func ConstructModule(tp node.Type, cfg *Config) fx.Option { return fx.Options() } + log.Warn("WARNING: gateway module is deprecated and will be removed in an upcoming release.") baseComponents := fx.Options( fx.Supply(cfg), fx.Error(cfgErr), diff --git a/nodebuilder/header/header.go b/nodebuilder/header/header.go index f807796eb6..f1eb4200c6 100644 --- a/nodebuilder/header/header.go +++ b/nodebuilder/header/header.go @@ -46,7 +46,6 @@ type Module interface { } // API is a wrapper around Module for the RPC. -// TODO(@distractedm1nd): These structs need to be autogenerated. type API struct { Internal struct { LocalHead func(context.Context) (*header.ExtendedHeader, error) `perm:"read"` diff --git a/nodebuilder/header/service.go b/nodebuilder/header/service.go index 944562ee61..edf5037e75 100644 --- a/nodebuilder/header/service.go +++ b/nodebuilder/header/service.go @@ -81,8 +81,6 @@ func (s *Service) GetByHeight(ctx context.Context, height uint64) (*header.Exten "networkHeight: %d, requestedHeight: %d", head.Height(), height) } - // TODO(vgonkivs): remove after https://github.com/celestiaorg/go-header/issues/32 is - // implemented and fetch header from HeaderEx if missing locally head, err = s.store.Head(ctx) switch { case err != nil: diff --git a/nodebuilder/init.go b/nodebuilder/init.go index 6c0854ff91..9194a092e0 100644 --- a/nodebuilder/init.go +++ b/nodebuilder/init.go @@ -10,8 +10,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/gofrs/flock" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" "github.com/celestiaorg/celestia-node/libs/utils" "github.com/celestiaorg/celestia-node/nodebuilder/node" diff --git a/nodebuilder/init_test.go b/nodebuilder/init_test.go index 99c93d7878..2058a00574 100644 --- a/nodebuilder/init_test.go +++ b/nodebuilder/init_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" "github.com/celestiaorg/celestia-node/nodebuilder/node" ) diff --git a/nodebuilder/node.go b/nodebuilder/node.go index c0ba8f78e8..b328d8c590 100644 --- a/nodebuilder/node.go +++ b/nodebuilder/node.go @@ -31,7 +31,7 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/p2p" "github.com/celestiaorg/celestia-node/nodebuilder/share" "github.com/celestiaorg/celestia-node/nodebuilder/state" - "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/store" ) var ( @@ -59,13 +59,13 @@ type Node struct { GatewayServer *gateway.Server `optional:"true"` // block store - EDSStore *eds.Store `optional:"true"` + EDSStore *store.Store `optional:"true"` // p2p components Host host.Host ConnGater *conngater.BasicConnectionGater Routing routing.PeerRouting - DataExchange exchange.Interface + DataExchange exchange.SessionExchange BlockService blockservice.BlockService // p2p protocols PubSub *pubsub.PubSub diff --git a/nodebuilder/node/admin.go b/nodebuilder/node/admin.go index fb21962c96..d8b2e10a73 100644 --- a/nodebuilder/node/admin.go +++ b/nodebuilder/node/admin.go @@ -2,6 +2,7 @@ package node import ( "context" + "time" "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" @@ -57,5 +58,11 @@ func (m *module) AuthVerify(_ context.Context, token string) ([]auth.Permission, } func (m *module) AuthNew(_ context.Context, permissions []auth.Permission) (string, error) { - return authtoken.NewSignedJWT(m.signer, permissions) + return authtoken.NewSignedJWT(m.signer, permissions, 0) +} + +func (m *module) AuthNewWithExpiry(_ context.Context, + permissions []auth.Permission, ttl time.Duration, +) (string, error) { + return authtoken.NewSignedJWT(m.signer, permissions, ttl) } diff --git a/nodebuilder/node/cmd/node.go b/nodebuilder/node/cmd/node.go index bef76b9683..cbb2ad8b34 100644 --- a/nodebuilder/node/cmd/node.go +++ b/nodebuilder/node/cmd/node.go @@ -87,8 +87,8 @@ var authCmd = &cobra.Command{ Use: "set-permissions", Args: cobra.MinimumNArgs(1), Short: "Signs and returns a new token with the given permissions.", - RunE: func(c *cobra.Command, args []string) error { - client, err := cmdnode.ParseClientFromCtx(c.Context()) + RunE: func(cmd *cobra.Command, args []string) error { + client, err := cmdnode.ParseClientFromCtx(cmd.Context()) if err != nil { return err } @@ -99,7 +99,13 @@ var authCmd = &cobra.Command{ perms[i] = (auth.Permission)(p) } - result, err := client.Node.AuthNew(c.Context(), perms) + ttl, _ := cmd.Flags().GetDuration("ttl") + if ttl != 0 { + result, err := client.Node.AuthNewWithExpiry(cmd.Context(), perms, ttl) + return cmdnode.PrintOutput(result, err, nil) + } + + result, err := client.Node.AuthNew(cmd.Context(), perms) return cmdnode.PrintOutput(result, err, nil) }, } diff --git a/nodebuilder/node/mocks/api.go b/nodebuilder/node/mocks/api.go index d8789a771c..3d284c9e06 100644 --- a/nodebuilder/node/mocks/api.go +++ b/nodebuilder/node/mocks/api.go @@ -94,3 +94,18 @@ func (mr *MockModuleMockRecorder) LogLevelSet(arg0, arg1, arg2 interface{}) *gom mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogLevelSet", reflect.TypeOf((*MockModule)(nil).LogLevelSet), arg0, arg1, arg2) } + +// Ready mocks base method. +func (m *MockModule) Ready(arg0 context.Context) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Ready", arg0) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Ready indicates an expected call of Ready. +func (mr *MockModuleMockRecorder) Ready(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ready", reflect.TypeOf((*MockModule)(nil).Ready), arg0) +} diff --git a/nodebuilder/node/node.go b/nodebuilder/node/node.go index b2bc7dac31..b146a7a40f 100644 --- a/nodebuilder/node/node.go +++ b/nodebuilder/node/node.go @@ -2,6 +2,7 @@ package node import ( "context" + "time" "github.com/filecoin-project/go-jsonrpc/auth" ) @@ -24,17 +25,20 @@ type Module interface { AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) // AuthNew signs and returns a new token with the given permissions. AuthNew(ctx context.Context, perms []auth.Permission) (string, error) + // AuthNewWithExpiry signs and returns a new token with the given permissions and TTL. + AuthNewWithExpiry(ctx context.Context, perms []auth.Permission, ttl time.Duration) (string, error) } var _ Module = (*API)(nil) type API struct { Internal struct { - Info func(context.Context) (Info, error) `perm:"admin"` - Ready func(context.Context) (bool, error) `perm:"read"` - LogLevelSet func(ctx context.Context, name, level string) error `perm:"admin"` - AuthVerify func(ctx context.Context, token string) ([]auth.Permission, error) `perm:"admin"` - AuthNew func(ctx context.Context, perms []auth.Permission) (string, error) `perm:"admin"` + Info func(context.Context) (Info, error) `perm:"admin"` + Ready func(context.Context) (bool, error) `perm:"read"` + LogLevelSet func(ctx context.Context, name, level string) error `perm:"admin"` + AuthVerify func(ctx context.Context, token string) ([]auth.Permission, error) `perm:"admin"` + AuthNew func(ctx context.Context, perms []auth.Permission) (string, error) `perm:"admin"` + AuthNewWithExpiry func(ctx context.Context, perms []auth.Permission, ttl time.Duration) (string, error) `perm:"admin"` } } @@ -57,3 +61,7 @@ func (api *API) AuthVerify(ctx context.Context, token string) ([]auth.Permission func (api *API) AuthNew(ctx context.Context, perms []auth.Permission) (string, error) { return api.Internal.AuthNew(ctx, perms) } + +func (api *API) AuthNewWithExpiry(ctx context.Context, perms []auth.Permission, ttl time.Duration) (string, error) { + return api.Internal.AuthNewWithExpiry(ctx, perms, ttl) +} diff --git a/nodebuilder/node/type.go b/nodebuilder/node/type.go index a86d802af1..97fed429d6 100644 --- a/nodebuilder/node/type.go +++ b/nodebuilder/node/type.go @@ -1,5 +1,9 @@ package node +import ( + "strings" +) + // Type defines the Node type (e.g. `light`, `bridge`) for identity purposes. // The zero value for Type is invalid. type Type uint8 @@ -36,7 +40,7 @@ func (t Type) IsValid() bool { // ParseType converts string in a type if possible. func ParseType(str string) Type { - tp, ok := stringToType[str] + tp, ok := stringToType[strings.ToLower(str)] if !ok { return 0 } @@ -53,9 +57,9 @@ var typeToString = map[Type]string{ // typeToString maps strings representations of all valid Types. var stringToType = map[string]Type{ - "Bridge": Bridge, - "Light": Light, - "Full": Full, + "bridge": Bridge, + "light": Light, + "full": Full, } // orderedTypes is a slice of all valid types in order of priority. diff --git a/nodebuilder/node_test.go b/nodebuilder/node_test.go index 833729dcda..bcf5f01942 100644 --- a/nodebuilder/node_test.go +++ b/nodebuilder/node_test.go @@ -15,9 +15,7 @@ import ( collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" "google.golang.org/protobuf/proto" - "github.com/celestiaorg/celestia-node/header/headertest" "github.com/celestiaorg/celestia-node/nodebuilder/node" - "github.com/celestiaorg/celestia-node/share" ) func TestLifecycle(t *testing.T) { @@ -125,34 +123,3 @@ func StartMockOtelCollectorHTTPServer(t *testing.T) (string, func()) { server.EnableHTTP2 = true return server.URL, server.Close } - -func TestEmptyBlockExists(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - test := []struct { - tp node.Type - }{ - {tp: node.Bridge}, - {tp: node.Full}, - // technically doesn't need to be tested as a SharesAvailable call to - // light node short circuits on an empty Root - {tp: node.Light}, - } - for i, tt := range test { - t.Run(strconv.Itoa(i), func(t *testing.T) { - node := TestNode(t, tt.tp) - err := node.Start(ctx) - require.NoError(t, err) - - // ensure an empty block exists in store - - eh := headertest.RandExtendedHeaderWithRoot(t, share.EmptyRoot()) - err = node.ShareServ.SharesAvailable(ctx, eh) - require.NoError(t, err) - - err = node.Stop(ctx) - require.NoError(t, err) - }) - } -} diff --git a/nodebuilder/p2p/bitswap.go b/nodebuilder/p2p/bitswap.go deleted file mode 100644 index 995800376e..0000000000 --- a/nodebuilder/p2p/bitswap.go +++ /dev/null @@ -1,94 +0,0 @@ -package p2p - -import ( - "context" - "fmt" - - "github.com/ipfs/boxo/bitswap" - "github.com/ipfs/boxo/bitswap/network" - "github.com/ipfs/boxo/blockstore" - "github.com/ipfs/boxo/exchange" - "github.com/ipfs/go-datastore" - metrics "github.com/ipfs/go-metrics-prometheus" - routinghelpers "github.com/libp2p/go-libp2p-routing-helpers" - hst "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/protocol" - "go.uber.org/fx" - - "github.com/celestiaorg/celestia-node/share/eds" -) - -const ( - // default size of bloom filter in blockStore - defaultBloomFilterSize = 512 << 10 - // default amount of hash functions defined for bloom filter - defaultBloomFilterHashes = 7 - // default size of arc cache in blockStore - defaultARCCacheSize = 64 << 10 -) - -// dataExchange provides a constructor for IPFS block's DataExchange over BitSwap. -func dataExchange(params bitSwapParams) exchange.Interface { - prefix := protocolID(params.Net) - net := network.NewFromIpfsHost(params.Host, &routinghelpers.Null{}, network.Prefix(prefix)) - - opts := []bitswap.Option{ - // Server options - bitswap.ProvideEnabled(false), // we don't provide blocks over DHT - // NOTE: These below are required for our protocol to work reliably. - // // See https://github.com/celestiaorg/celestia-node/issues/732 - bitswap.SetSendDontHaves(false), - - // Client options - bitswap.SetSimulateDontHavesOnTimeout(false), - bitswap.WithoutDuplicatedBlockStats(), - } - bs := bitswap.New(params.Ctx, net, params.Bs, opts...) - - params.Lifecycle.Append(fx.Hook{ - OnStop: func(_ context.Context) (err error) { - return bs.Close() - }, - }) - return bs -} - -func blockstoreFromDatastore(ctx context.Context, ds datastore.Batching) (blockstore.Blockstore, error) { - return blockstore.CachedBlockstore( - ctx, - blockstore.NewBlockstore(ds), - blockstore.CacheOpts{ - HasBloomFilterSize: defaultBloomFilterSize, - HasBloomFilterHashes: defaultBloomFilterHashes, - HasTwoQueueCacheSize: defaultARCCacheSize, - }, - ) -} - -func blockstoreFromEDSStore(ctx context.Context, store *eds.Store) (blockstore.Blockstore, error) { - return blockstore.CachedBlockstore( - ctx, - store.Blockstore(), - blockstore.CacheOpts{ - HasTwoQueueCacheSize: defaultARCCacheSize, - }, - ) -} - -type bitSwapParams struct { - fx.In - - Lifecycle fx.Lifecycle - Ctx context.Context - Net Network - Host hst.Host - Bs blockstore.Blockstore -} - -func protocolID(network Network) protocol.ID { - return protocol.ID(fmt.Sprintf("/celestia/%s", network)) -} - -func enableBitswapMetrics() { - _ = metrics.Inject() -} diff --git a/nodebuilder/p2p/bootstrap.go b/nodebuilder/p2p/bootstrap.go index 7a0045005c..6566750452 100644 --- a/nodebuilder/p2p/bootstrap.go +++ b/nodebuilder/p2p/bootstrap.go @@ -47,6 +47,8 @@ var bootstrapList = map[Network][]string{ "/dnsaddr/da-full-1.celestia-bootstrap.net/p2p/12D3KooWKZCMcwGCYbL18iuw3YVpAZoyb1VBGbx9Kapsjw3soZgr", "/dnsaddr/da-full-2.celestia-bootstrap.net/p2p/12D3KooWE3fmRtHgfk9DCuQFfY3H3JYEnTU3xZozv1Xmo8KWrWbK", "/dnsaddr/da-full-3.celestia-bootstrap.net/p2p/12D3KooWK6Ftsd4XsWCsQZgZPNhTrE5urwmkoo5P61tGvnKmNVyv", + "/dnsaddr/boot.celestia.pops.one/p2p/12D3KooWBBzzGy5hAHUQVh2vBvL25CKwJ7wbmooPcz4amQhzJHJq", + "/dnsaddr/celestia.qubelabs.io/p2p/12D3KooWAzucnC7yawvLbmVxv53ihhjbHFSVZCsPuuSzTg6A7wgx", }, Arabica: { "/dnsaddr/da-bridge-1.celestia-arabica-11.com/p2p/12D3KooWGqwzdEqM54Dce6LXzfFr97Bnhvm6rN7KM7MFwdomfm4S", diff --git a/nodebuilder/p2p/cmd/p2p.go b/nodebuilder/p2p/cmd/p2p.go index 64c36fc9d6..9cbf8357af 100644 --- a/nodebuilder/p2p/cmd/p2p.go +++ b/nodebuilder/p2p/cmd/p2p.go @@ -1,6 +1,8 @@ package cmd import ( + "time" + "github.com/libp2p/go-libp2p/core/metrics" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -9,6 +11,7 @@ import ( "github.com/spf13/cobra" cmdnode "github.com/celestiaorg/celestia-node/cmd" + "github.com/celestiaorg/celestia-node/nodebuilder/p2p" ) type peerInfo struct { @@ -34,6 +37,9 @@ func init() { peerBandwidthCmd, bandwidthForProtocolCmd, pubsubPeersCmd, + pubsubTopicsCmd, + connectionInfoCmd, + pingCmd, ) } @@ -574,3 +580,96 @@ var pubsubPeersCmd = &cobra.Command{ return cmdnode.PrintOutput(peers, err, formatter) }, } + +var pubsubTopicsCmd = &cobra.Command{ + Use: "pubsub-topics ", + Short: "Lists pubsub(GossipSub) topics the node participates in", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := cmdnode.ParseClientFromCtx(cmd.Context()) + if err != nil { + return err + } + defer client.Close() + + topics, err := client.P2P.PubSubTopics(cmd.Context()) + formatter := func(data interface{}) interface{} { + conPeers := data.([]string) + return struct { + Topics []string `json:"topics"` + }{ + Topics: conPeers, + } + } + return cmdnode.PrintOutput(topics, err, formatter) + }, +} + +var connectionInfoCmd = &cobra.Command{ + Use: "connection-state [peerID]", + Short: "Gets connection info for a given peer ID", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := cmdnode.ParseClientFromCtx(cmd.Context()) + if err != nil { + return err + } + defer client.Close() + + pid, err := peer.Decode(args[0]) + if err != nil { + return err + } + + infos, err := client.P2P.ConnectionState(cmd.Context(), pid) + return cmdnode.PrintOutput(infos, err, func(i interface{}) interface{} { + type state struct { + Info network.ConnectionState + NumStreams int + Direction string + Opened string + Limited bool + } + + states := i.([]p2p.ConnectionState) + infos := make([]state, len(states)) + for i, s := range states { + infos[i] = state{ + Info: s.Info, + NumStreams: s.NumStreams, + Direction: s.Direction.String(), + Opened: s.Opened.Format("2006-01-02 15:04:05"), + Limited: s.Limited, + } + } + + if len(infos) == 1 { + return infos[0] + } + return infos + }) + }, +} + +var pingCmd = &cobra.Command{ + Use: "ping [peerID]", + Short: "Pings given peer and tell how much time that took or errors", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := cmdnode.ParseClientFromCtx(cmd.Context()) + if err != nil { + return err + } + defer client.Close() + + pid, err := peer.Decode(args[0]) + if err != nil { + return err + } + + pingDuration, err := client.P2P.Ping(cmd.Context(), pid) + return cmdnode.PrintOutput(pingDuration, err, func(i interface{}) interface{} { + return i.(time.Duration).String() + }) + }, +} diff --git a/nodebuilder/p2p/config.go b/nodebuilder/p2p/config.go index 5276ff9acb..32c60846f4 100644 --- a/nodebuilder/p2p/config.go +++ b/nodebuilder/p2p/config.go @@ -2,7 +2,6 @@ package p2p import ( "fmt" - "time" "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" @@ -10,8 +9,6 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/node" ) -const defaultRoutingRefreshPeriod = time.Minute - // Config combines all configuration fields for P2P subsystem. type Config struct { // ListenAddresses - Addresses to listen to on local NIC. @@ -29,8 +26,7 @@ type Config struct { // This is enabled by default for Bootstrappers. PeerExchange bool // ConnManager is a configuration tuple for ConnectionManager. - ConnManager connManagerConfig - RoutingTableRefreshPeriod time.Duration + ConnManager connManagerConfig // Allowlist for IPColocation PubSub parameter, a list of string CIDRs IPColocationWhitelist []string @@ -64,10 +60,9 @@ func DefaultConfig(tp node.Type) Config { "/ip4/127.0.0.1/tcp/2121", "/ip6/::/tcp/2121", }, - MutualPeers: []string{}, - PeerExchange: tp == node.Bridge || tp == node.Full, - ConnManager: defaultConnManagerConfig(tp), - RoutingTableRefreshPeriod: defaultRoutingRefreshPeriod, + MutualPeers: []string{}, + PeerExchange: tp == node.Bridge || tp == node.Full, + ConnManager: defaultConnManagerConfig(tp), } } @@ -83,15 +78,6 @@ func (cfg *Config) mutualPeers() (_ []peer.AddrInfo, err error) { return peer.AddrInfosFromP2pAddrs(maddrs...) } -// Validate performs basic validation of the config. -func (cfg *Config) Validate() error { - if cfg.RoutingTableRefreshPeriod <= 0 { - cfg.RoutingTableRefreshPeriod = defaultRoutingRefreshPeriod - log.Warnf("routingTableRefreshPeriod is not valid. restoring to default value: %d", cfg.RoutingTableRefreshPeriod) - } - return nil -} - // Upgrade updates the `ListenAddresses` and `NoAnnounceAddresses` to // include support for websocket connections. func (cfg *Config) Upgrade() { diff --git a/nodebuilder/p2p/host.go b/nodebuilder/p2p/host.go index 913591b37d..1003970199 100644 --- a/nodebuilder/p2p/host.go +++ b/nodebuilder/p2p/host.go @@ -104,8 +104,8 @@ func host(params hostParams) (HostBase, error) { libp2p.DefaultMuxers, } - if params.Registry != nil { - opts = append(opts, libp2p.PrometheusRegisterer(params.Registry)) + if params.Registerer != nil { + opts = append(opts, libp2p.PrometheusRegisterer(params.Registerer)) } else { opts = append(opts, libp2p.DisableMetrics()) } @@ -143,7 +143,7 @@ type hostParams struct { ConnGater *conngater.BasicConnectionGater Bandwidth *metrics.BandwidthCounter ResourceManager network.ResourceManager - Registry prometheus.Registerer `optional:"true"` + Registerer prometheus.Registerer `optional:"true"` Tp node.Type } diff --git a/nodebuilder/p2p/metrics.go b/nodebuilder/p2p/metrics.go index 606e5d39f9..0372ccc408 100644 --- a/nodebuilder/p2p/metrics.go +++ b/nodebuilder/p2p/metrics.go @@ -18,8 +18,7 @@ import ( func WithMetrics() fx.Option { return fx.Options( fx.Provide(resourceManagerOpt(traceReporter)), - fx.Invoke(prometheusMetrics), - fx.Invoke(enableBitswapMetrics), + fx.Provide(prometheusMetrics), ) } @@ -37,7 +36,7 @@ func prometheusMetrics(lifecycle fx.Lifecycle, peerID peer.ID, nodeType node.Type, network Network, -) error { +) (prometheus.Registerer, error) { reg := prometheus.NewRegistry() labels := prometheus.Labels{ networkLabel: network.String(), @@ -45,6 +44,10 @@ func prometheusMetrics(lifecycle fx.Lifecycle, peerIDLabel: peerID.String(), } wrapped := prometheus.WrapRegistererWith(labels, reg) + // Set the default global registerer to the wrapped one with labels. This way all the metrics + // registered with the default registerer will be labeled with the provided labels. It is important + // because unlike libp2p metrics, bitswap metrics are registered with the default global registerer. + prometheus.DefaultRegisterer = wrapped mux := http.NewServeMux() handler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: wrapped}) @@ -72,5 +75,5 @@ func prometheusMetrics(lifecycle fx.Lifecycle, return promHTTPServer.Shutdown(ctx) }, }) - return nil + return wrapped, nil } diff --git a/nodebuilder/p2p/misc.go b/nodebuilder/p2p/misc.go index bcbc82152f..2dcd0f9190 100644 --- a/nodebuilder/p2p/misc.go +++ b/nodebuilder/p2p/misc.go @@ -7,7 +7,7 @@ import ( "github.com/ipfs/go-datastore" connmgri "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/peerstore" - "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds" //nolint:staticcheck //nolint:nolintlint + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds" //nolint:staticcheck "github.com/libp2p/go-libp2p/p2p/net/conngater" "github.com/libp2p/go-libp2p/p2p/net/connmgr" diff --git a/nodebuilder/p2p/mocks/api.go b/nodebuilder/p2p/mocks/api.go index aa5083199f..8ef5638d52 100644 --- a/nodebuilder/p2p/mocks/api.go +++ b/nodebuilder/p2p/mocks/api.go @@ -7,7 +7,9 @@ package mocks import ( context "context" reflect "reflect" + time "time" + p2p "github.com/celestiaorg/celestia-node/nodebuilder/p2p" gomock "github.com/golang/mock/gomock" metrics "github.com/libp2p/go-libp2p/core/metrics" network "github.com/libp2p/go-libp2p/core/network" @@ -141,6 +143,21 @@ func (mr *MockModuleMockRecorder) Connectedness(arg0, arg1 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connectedness", reflect.TypeOf((*MockModule)(nil).Connectedness), arg0, arg1) } +// ConnectionState mocks base method. +func (m *MockModule) ConnectionState(arg0 context.Context, arg1 peer.ID) ([]p2p.ConnectionState, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConnectionState", arg0, arg1) + ret0, _ := ret[0].([]p2p.ConnectionState) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ConnectionState indicates an expected call of ConnectionState. +func (mr *MockModuleMockRecorder) ConnectionState(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionState", reflect.TypeOf((*MockModule)(nil).ConnectionState), arg0, arg1) +} + // Info mocks base method. func (m *MockModule) Info(arg0 context.Context) (peer.AddrInfo, error) { m.ctrl.T.Helper() @@ -231,6 +248,21 @@ func (mr *MockModuleMockRecorder) Peers(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Peers", reflect.TypeOf((*MockModule)(nil).Peers), arg0) } +// Ping mocks base method. +func (m *MockModule) Ping(arg0 context.Context, arg1 peer.ID) (time.Duration, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Ping", arg0, arg1) + ret0, _ := ret[0].(time.Duration) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Ping indicates an expected call of Ping. +func (mr *MockModuleMockRecorder) Ping(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockModule)(nil).Ping), arg0, arg1) +} + // Protect mocks base method. func (m *MockModule) Protect(arg0 context.Context, arg1 peer.ID, arg2 string) error { m.ctrl.T.Helper() @@ -260,6 +292,21 @@ func (mr *MockModuleMockRecorder) PubSubPeers(arg0, arg1 interface{}) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PubSubPeers", reflect.TypeOf((*MockModule)(nil).PubSubPeers), arg0, arg1) } +// PubSubTopics mocks base method. +func (m *MockModule) PubSubTopics(arg0 context.Context) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PubSubTopics", arg0) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PubSubTopics indicates an expected call of PubSubTopics. +func (mr *MockModuleMockRecorder) PubSubTopics(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PubSubTopics", reflect.TypeOf((*MockModule)(nil).PubSubTopics), arg0) +} + // ResourceState mocks base method. func (m *MockModule) ResourceState(arg0 context.Context) (rcmgr.ResourceManagerStat, error) { m.ctrl.T.Helper() diff --git a/nodebuilder/p2p/module.go b/nodebuilder/p2p/module.go index 59d9fa5054..9538ce03a5 100644 --- a/nodebuilder/p2p/module.go +++ b/nodebuilder/p2p/module.go @@ -14,9 +14,7 @@ var log = logging.Logger("module/p2p") // ConstructModule collects all the components and services related to p2p. func ConstructModule(tp node.Type, cfg *Config) fx.Option { // sanitize config values before constructing module - cfgErr := cfg.Validate() baseComponents := fx.Options( - fx.Error(cfgErr), fx.Supply(cfg), fx.Provide(Key), fx.Provide(id), @@ -26,10 +24,9 @@ func ConstructModule(tp node.Type, cfg *Config) fx.Option { fx.Provide(host), fx.Provide(routedHost), fx.Provide(pubSub), - fx.Provide(dataExchange), fx.Provide(ipld.NewBlockservice), fx.Provide(peerRouting), - fx.Provide(contentRouting), + fx.Provide(newDHT), fx.Provide(addrsFactory(cfg.AnnounceAddresses, cfg.NoAnnounceAddresses)), fx.Provide(metrics.NewBandwidthCounter), fx.Provide(newModule), @@ -43,14 +40,12 @@ func ConstructModule(tp node.Type, cfg *Config) fx.Option { return fx.Module( "p2p", baseComponents, - fx.Provide(blockstoreFromEDSStore), fx.Provide(infiniteResources), ) case node.Light: return fx.Module( "p2p", baseComponents, - fx.Provide(blockstoreFromDatastore), fx.Provide(autoscaleResources), ) default: diff --git a/nodebuilder/p2p/network.go b/nodebuilder/p2p/network.go index 4f83eea213..d82f1d1a55 100644 --- a/nodebuilder/p2p/network.go +++ b/nodebuilder/p2p/network.go @@ -69,6 +69,11 @@ var networkAliases = map[string]Network{ "private": Private, } +// GetNetwork returns the Network for the given string representation. +func GetNetwork(networkStr string) Network { + return networkAliases[networkStr] +} + // orderedNetworks is a list of all known networks in order of priority. var orderedNetworks = []Network{Mainnet, Mocha, Arabica, Private} @@ -78,15 +83,21 @@ func GetNetworks() []Network { } // listAvailableNetworks provides a string listing all known long-standing networks for things -// like CLI hints. +// like CLI hints. It also lists the network aliases as valid arguments. func listAvailableNetworks() string { var networks []string + for _, net := range orderedNetworks { // "private" networks are configured via env vars, so skip if net != Private { networks = append(networks, net.String()) } } + for net := range networkAliases { + if net != "private" { + networks = append(networks, net) + } + } return strings.Join(networks, ", ") } diff --git a/nodebuilder/p2p/p2p.go b/nodebuilder/p2p/p2p.go index 236937e89a..e98ece9ba8 100644 --- a/nodebuilder/p2p/p2p.go +++ b/nodebuilder/p2p/p2p.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "reflect" + "time" pubsub "github.com/libp2p/go-libp2p-pubsub" libhost "github.com/libp2p/go-libp2p/core/host" @@ -14,14 +15,29 @@ import ( "github.com/libp2p/go-libp2p/p2p/host/autonat" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" "github.com/libp2p/go-libp2p/p2p/net/conngater" + "github.com/libp2p/go-libp2p/p2p/protocol/ping" ) var _ Module = (*API)(nil) +// ConnectionState holds information about a connection. +type ConnectionState struct { + Info network.ConnectionState + // NumStreams is the number of streams on the connection. + NumStreams int + // Direction specifies whether this is an inbound or an outbound connection. + Direction network.Direction + // Opened is the timestamp when this connection was opened. + Opened time.Time + // Limited indicates that this connection is Limited. It maybe limited by + // bytes or time. In practice, this is a connection formed over a circuit v2 + // relay. + Limited bool +} + // Module represents all accessible methods related to the node's p2p // host / operations. // -//nolint:dupl //go:generate mockgen -destination=mocks/api.go -package=mocks . Module type Module interface { // Info returns address information about the host. @@ -39,10 +55,14 @@ type Module interface { ClosePeer(ctx context.Context, id peer.ID) error // Connectedness returns a state signaling connection capabilities. Connectedness(ctx context.Context, id peer.ID) (network.Connectedness, error) + // ConnectionState returns information about each *active* connection to the peer. + // NOTE: At most cases there should be only a single connection. + ConnectionState(ctx context.Context, id peer.ID) ([]ConnectionState, error) // NATStatus returns the current NAT status. NATStatus(context.Context) (network.Reachability, error) - // BlockPeer adds a peer to the set of blocked peers. + // BlockPeer adds a peer to the set of blocked peers and + // closes any existing connection to that peer. BlockPeer(ctx context.Context, p peer.ID) error // UnblockPeer removes a peer from the set of blocked peers. UnblockPeer(ctx context.Context, p peer.ID) error @@ -77,6 +97,11 @@ type Module interface { // PubSubPeers returns the peer IDs of the peers joined on // the given topic. PubSubPeers(ctx context.Context, topic string) ([]peer.ID, error) + // PubSubTopics reports current PubSubTopics the node participates in. + PubSubTopics(ctx context.Context) ([]string, error) + + // Ping pings the selected peer and returns time it took or error. + Ping(ctx context.Context, peer peer.ID) (time.Duration, error) } // module contains all components necessary to access information and @@ -143,7 +168,13 @@ func (m *module) NATStatus(context.Context) (network.Reachability, error) { } func (m *module) BlockPeer(_ context.Context, p peer.ID) error { - return m.connGater.BlockPeer(p) + if err := m.connGater.BlockPeer(p); err != nil { + return err + } + if err := m.host.Network().ClosePeer(p); err != nil { + log.Warnf("failed to close connection to blocked peer %s: %v", p, err) + } + return nil } func (m *module) UnblockPeer(_ context.Context, p peer.ID) error { @@ -192,10 +223,37 @@ func (m *module) PubSubPeers(_ context.Context, topic string) ([]peer.ID, error) return m.ps.ListPeers(topic), nil } +func (m *module) PubSubTopics(_ context.Context) ([]string, error) { + return m.ps.GetTopics(), nil +} + +func (m *module) Ping(ctx context.Context, peer peer.ID) (time.Duration, error) { + res := <-ping.Ping(ctx, m.host, peer) // context is handled for us + return res.RTT, res.Error +} + +func (m *module) ConnectionState(_ context.Context, peer peer.ID) ([]ConnectionState, error) { + cons := m.host.Network().ConnsToPeer(peer) + if len(cons) == 0 { + return nil, fmt.Errorf("no connections to peer %s", peer) + } + + conInfos := make([]ConnectionState, len(cons)) + for i, con := range cons { + stat := con.Stat() + conInfos[i] = ConnectionState{ + Info: con.ConnState(), + NumStreams: stat.NumStreams, + Direction: stat.Direction, + Opened: stat.Opened, + Limited: stat.Limited, + } + } + + return conInfos, nil +} + // API is a wrapper around Module for the RPC. -// TODO(@distractedm1nd): These structs need to be autogenerated. -// -//nolint:dupl type API struct { Internal struct { Info func(context.Context) (peer.AddrInfo, error) `perm:"admin"` @@ -216,6 +274,9 @@ type API struct { BandwidthForProtocol func(ctx context.Context, proto protocol.ID) (metrics.Stats, error) `perm:"admin"` ResourceState func(context.Context) (rcmgr.ResourceManagerStat, error) `perm:"admin"` PubSubPeers func(ctx context.Context, topic string) ([]peer.ID, error) `perm:"admin"` + PubSubTopics func(ctx context.Context) ([]string, error) `perm:"admin"` + Ping func(ctx context.Context, peer peer.ID) (time.Duration, error) `perm:"admin"` + ConnectionState func(context.Context, peer.ID) ([]ConnectionState, error) `perm:"admin"` } } @@ -290,3 +351,15 @@ func (api *API) ResourceState(ctx context.Context) (rcmgr.ResourceManagerStat, e func (api *API) PubSubPeers(ctx context.Context, topic string) ([]peer.ID, error) { return api.Internal.PubSubPeers(ctx, topic) } + +func (api *API) PubSubTopics(ctx context.Context) ([]string, error) { + return api.Internal.PubSubTopics(ctx) +} + +func (api *API) Ping(ctx context.Context, peer peer.ID) (time.Duration, error) { + return api.Internal.Ping(ctx, peer) +} + +func (api *API) ConnectionState(ctx context.Context, peer peer.ID) ([]ConnectionState, error) { + return api.Internal.ConnectionState(ctx, peer) +} diff --git a/nodebuilder/p2p/p2p_test.go b/nodebuilder/p2p/p2p_test.go index b883889859..b889663c20 100644 --- a/nodebuilder/p2p/p2p_test.go +++ b/nodebuilder/p2p/p2p_test.go @@ -41,6 +41,11 @@ func TestP2PModule_Host(t *testing.T) { connectedness, err := mgr.Connectedness(ctx, peer.ID()) require.NoError(t, err) + + infos, err := mgr.ConnectionState(ctx, peer.ID()) + require.NoError(t, err) + require.GreaterOrEqual(t, len(infos), 1) + assert.Equal(t, host.Network().Connectedness(peer.ID()), connectedness) // now disconnect using manager and check for connectedness match again assert.NoError(t, mgr.ClosePeer(ctx, peer.ID())) @@ -218,22 +223,36 @@ func TestP2PModule_Pubsub(t *testing.T) { // TestP2PModule_ConnGater tests P2P Module methods on // the instance of ConnectionGater. func TestP2PModule_ConnGater(t *testing.T) { + net, err := mocknet.FullMeshConnected(2) + require.NoError(t, err) + host, peer := net.Hosts()[0], net.Hosts()[1] + gater, err := connectionGater(datastore.NewMapDatastore()) require.NoError(t, err) - mgr := newModule(nil, nil, gater, nil, nil) + mgr := newModule(host, nil, gater, nil, nil) ctx := context.Background() - assert.NoError(t, mgr.BlockPeer(ctx, "badpeer")) + assert.NoError(t, mgr.BlockPeer(ctx, peer.ID())) blocked, err := mgr.ListBlockedPeers(ctx) require.NoError(t, err) - assert.Len(t, blocked, 1) + assert.Contains(t, blocked, peer.ID()) - assert.NoError(t, mgr.UnblockPeer(ctx, "badpeer")) + // Check if the peer is disconnected (or remains disconnected) after blocking + connectedness, err := mgr.Connectedness(ctx, peer.ID()) + require.NoError(t, err) + assert.Equal(t, network.NotConnected, connectedness) + + assert.NoError(t, mgr.UnblockPeer(ctx, peer.ID())) blocked, err = mgr.ListBlockedPeers(ctx) require.NoError(t, err) - assert.Len(t, blocked, 0) + assert.NotContains(t, blocked, peer.ID()) + + // Verify that unblocking doesn't automatically reconnect + connectedness, err = mgr.Connectedness(ctx, peer.ID()) + require.NoError(t, err) + assert.Equal(t, network.NotConnected, connectedness) } // TestP2PModule_ResourceManager tests P2P Module methods on diff --git a/nodebuilder/p2p/routing.go b/nodebuilder/p2p/routing.go index edbf624b08..53f5377524 100644 --- a/nodebuilder/p2p/routing.go +++ b/nodebuilder/p2p/routing.go @@ -6,70 +6,52 @@ import ( "github.com/ipfs/go-datastore" dht "github.com/libp2p/go-libp2p-kad-dht" - "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/core/routing" "go.uber.org/fx" "github.com/celestiaorg/celestia-node/nodebuilder/node" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/discovery" ) -// contentRouting constructs nil content routing, -// as for our use-case existing ContentRouting mechanisms, e.g DHT, are unsuitable -func contentRouting(r routing.PeerRouting) routing.ContentRouting { - return r.(*dht.IpfsDHT) -} - -// peerRouting provides constructor for PeerRouting over DHT. -// Basically, this provides a way to discover peer addresses by respecting public keys. -func peerRouting(cfg *Config, tp node.Type, params routingParams) (routing.PeerRouting, error) { - opts := []dht.Option{ - dht.BootstrapPeers(params.Peers...), - dht.ProtocolPrefix(protocol.ID(fmt.Sprintf("/celestia/%s", params.Net))), - dht.Datastore(params.DataStore), - dht.RoutingTableRefreshPeriod(cfg.RoutingTableRefreshPeriod), - } - - if isBootstrapper() { - opts = append(opts, - dht.BootstrapPeers(), // no bootstrappers for a bootstrapper ¯\_(ツ)_/¯ - ) - } - +func newDHT( + ctx context.Context, + lc fx.Lifecycle, + tp node.Type, + network Network, + bootsrappers Bootstrappers, + host HostBase, + dataStore datastore.Batching, +) (*dht.IpfsDHT, error) { + var mode dht.ModeOpt switch tp { case node.Light: - opts = append(opts, - dht.Mode(dht.ModeClient), - ) + mode = dht.ModeClient case node.Bridge, node.Full: - opts = append(opts, - dht.Mode(dht.ModeServer), - ) + mode = dht.ModeServer default: return nil, fmt.Errorf("unsupported node type: %s", tp) } - d, err := dht.New(params.Ctx, params.Host, opts...) + // no bootstrappers for a bootstrapper ¯\_(ツ)_/¯ + // otherwise dht.Bootstrap(OnStart hook) will deadlock + if isBootstrapper() { + bootsrappers = nil + } + + dht, err := discovery.NewDHT(ctx, network.String(), bootsrappers, host, dataStore, mode) if err != nil { return nil, err } - params.Lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - return d.Bootstrap(ctx) - }, - OnStop: func(context.Context) error { - return d.Close() - }, + stopFn := func(context.Context) error { + return dht.Close() + } + lc.Append(fx.Hook{ + OnStart: dht.Bootstrap, + OnStop: stopFn, }) - return d, nil + return dht, nil } -type routingParams struct { - fx.In - - Ctx context.Context - Net Network - Peers Bootstrappers - Lc fx.Lifecycle - Host HostBase - DataStore datastore.Batching +func peerRouting(dht *dht.IpfsDHT) routing.PeerRouting { + return dht } diff --git a/nodebuilder/pruner/constructors.go b/nodebuilder/pruner/constructors.go index 4b3fa44f79..e391c82f60 100644 --- a/nodebuilder/pruner/constructors.go +++ b/nodebuilder/pruner/constructors.go @@ -7,17 +7,18 @@ import ( "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" + modshare "github.com/celestiaorg/celestia-node/nodebuilder/share" "github.com/celestiaorg/celestia-node/pruner" ) func newPrunerService( p pruner.Pruner, - window pruner.AvailabilityWindow, + window modshare.Window, getter libhead.Store[*header.ExtendedHeader], ds datastore.Batching, opts ...pruner.Option, ) (*pruner.Service, error) { - serv, err := pruner.NewService(p, window, getter, ds, p2p.BlockTime, opts...) + serv, err := pruner.NewService(p, window.Duration(), getter, ds, p2p.BlockTime, opts...) if err != nil { return nil, err } diff --git a/nodebuilder/pruner/flags.go b/nodebuilder/pruner/flags.go index 7734c49e46..986fb9eaf5 100644 --- a/nodebuilder/pruner/flags.go +++ b/nodebuilder/pruner/flags.go @@ -3,18 +3,53 @@ package pruner import ( "github.com/spf13/cobra" flag "github.com/spf13/pflag" + + "github.com/celestiaorg/celestia-node/nodebuilder/node" ) -const pruningFlag = "experimental-pruning" +const ( + pruningFlag = "experimental-pruning" + archivalFlag = "archival" +) func Flags() *flag.FlagSet { flags := &flag.FlagSet{} - flags.Bool(pruningFlag, false, "EXPERIMENTAL: Enables pruning of blocks outside the pruning window.") + flags.Bool(pruningFlag, false, "EXPERIMENTAL: Enables pruning of blocks outside the pruning window."+ + " Warning: flag will be removed in an upcoming release and pruning will become the default mode for all nodes.") + flags.Bool(archivalFlag, false, "Enables archival mode, which disables pruning and enables the storage of all blocks.") return flags } -func ParseFlags(cmd *cobra.Command, cfg *Config) { - cfg.EnableService = cmd.Flag(pruningFlag).Changed +func ParseFlags(cmd *cobra.Command, cfg *Config, tp node.Type) { + pruningChanged := cmd.Flag(pruningFlag).Changed + archivalChanged := cmd.Flag(archivalFlag).Changed + + cfg.EnableService = pruningChanged + + // Validate archival flag usage early to prevent invalid configurations + if archivalChanged { + if tp != node.Full && tp != node.Bridge { + log.Fatal("Archival mode is only supported for Full and Bridge nodes") + } + if cfg.EnableService { + log.Fatal("Cannot enable both pruning and archival modes") + } + } + + // Add pruning flag deprecation warning + if cfg.EnableService { + log.Warn(`WARNING: --experimental-pruning flag will be removed in an upcoming release. +Pruning will become the default mode for all nodes. +If you want to retain history beyond the sampling window, please pass the --archival flag.`) + return + } + + // Warn the user if pruning is disabled and archival is not enabled for Full and Bridge nodes + if !archivalChanged && (tp == node.Full || tp == node.Bridge) { + log.Warn(`WARNING: Pruning is disabled. +Pruning will become the default mode for all nodes. +If you want to retain history beyond the sampling window, please pass the --archival flag.`) + } } diff --git a/nodebuilder/pruner/migration_utils.go b/nodebuilder/pruner/migration_utils.go new file mode 100644 index 0000000000..cccf125e7f --- /dev/null +++ b/nodebuilder/pruner/migration_utils.go @@ -0,0 +1,59 @@ +package pruner + +import ( + "context" + "fmt" + + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" + + fullavail "github.com/celestiaorg/celestia-node/share/availability/full" +) + +// TODO @renaynay: remove this file after a few releases -- this utility serves as a temporary solution +// to detect if the node has been run with pruning enabled before on previous version(s), and disallow +// running as an archival node. + +var ( + storePrefix = datastore.NewKey("full_avail") + previousModeKey = datastore.NewKey("previous_mode") +) + +// detectFirstRun is a temporary function that serves to assist migration to the refactored pruner +// implementation (v0.21.0). It checks if the node has been run with pruning enabled before by checking +// if the pruner service ran before, and disallows running as an archival node in the case it has. +func detectFirstRun(ctx context.Context, cfg *Config, ds datastore.Datastore, lastPrunedHeight uint64) error { + ds = namespace.Wrap(ds, storePrefix) + + exists, err := ds.Has(ctx, previousModeKey) + if err != nil { + return fmt.Errorf("share/availability/full: failed to check previous pruned run in "+ + "datastore: %w", err) + } + if exists { + // node has already been run on current version, no migration is necessary + return nil + } + + isArchival := !cfg.EnableService + + // if the node has been pruned before on a previous version, it cannot revert + // to archival mode + if isArchival && lastPrunedHeight > 1 { + return fullavail.ErrDisallowRevertToArchival + } + + return recordFirstRun(ctx, ds, isArchival) +} + +// recordFirstRun exists to assist migration to new pruner implementation (v0.21.0) by recording +// the first run of the pruner service in the full availability's datastore. It assumes the datastore +// is already namespace-wrapped. +func recordFirstRun(ctx context.Context, ds datastore.Datastore, isArchival bool) error { + mode := []byte("pruned") + if isArchival { + mode = []byte("archival") + } + + return ds.Put(ctx, previousModeKey, mode) +} diff --git a/nodebuilder/pruner/migration_utils_test.go b/nodebuilder/pruner/migration_utils_test.go new file mode 100644 index 0000000000..bedd0fdfcf --- /dev/null +++ b/nodebuilder/pruner/migration_utils_test.go @@ -0,0 +1,60 @@ +package pruner + +import ( + "context" + "testing" + + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" + ds_sync "github.com/ipfs/go-datastore/sync" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + fullavail "github.com/celestiaorg/celestia-node/share/availability/full" +) + +func TestDetectFirstRun(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + t.Run("FirstRunArchival", func(t *testing.T) { + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + + cfg := &Config{EnableService: false} + + err := detectFirstRun(ctx, cfg, ds, 1) + assert.NoError(t, err) + + nsWrapped := namespace.Wrap(ds, storePrefix) + prevMode, err := nsWrapped.Get(ctx, previousModeKey) + require.NoError(t, err) + assert.Equal(t, []byte("archival"), prevMode) + }) + + t.Run("FirstRunPruned", func(t *testing.T) { + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + + cfg := &Config{EnableService: true} + + err := detectFirstRun(ctx, cfg, ds, 1) + assert.NoError(t, err) + + nsWrapped := namespace.Wrap(ds, storePrefix) + prevMode, err := nsWrapped.Get(ctx, previousModeKey) + require.NoError(t, err) + assert.Equal(t, []byte("pruned"), prevMode) + }) + + t.Run("RevertToArchivalNotAllowed", func(t *testing.T) { + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + + // create archival node instance over a node that has been pruned before + // (height 500) + cfg := &Config{EnableService: false} + lastPrunedHeight := uint64(500) + + err := detectFirstRun(ctx, cfg, ds, lastPrunedHeight) + assert.Error(t, err) + assert.ErrorIs(t, err, fullavail.ErrDisallowRevertToArchival) + }) +} diff --git a/nodebuilder/pruner/module.go b/nodebuilder/pruner/module.go index 7475195307..ce53239666 100644 --- a/nodebuilder/pruner/module.go +++ b/nodebuilder/pruner/module.go @@ -4,23 +4,22 @@ import ( "context" "github.com/ipfs/go-datastore" + logging "github.com/ipfs/go-log/v2" "go.uber.org/fx" "github.com/celestiaorg/celestia-node/core" - "github.com/celestiaorg/celestia-node/libs/fxutil" "github.com/celestiaorg/celestia-node/nodebuilder/node" + modshare "github.com/celestiaorg/celestia-node/nodebuilder/share" "github.com/celestiaorg/celestia-node/pruner" - "github.com/celestiaorg/celestia-node/pruner/archival" - "github.com/celestiaorg/celestia-node/pruner/full" - "github.com/celestiaorg/celestia-node/pruner/light" + "github.com/celestiaorg/celestia-node/share/availability" + fullavail "github.com/celestiaorg/celestia-node/share/availability/full" + "github.com/celestiaorg/celestia-node/share/availability/light" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/discovery" ) -func ConstructModule(tp node.Type, cfg *Config) fx.Option { - baseComponents := fx.Options( - fx.Supply(cfg), - availWindow(tp, cfg.EnableService), - ) +var log = logging.Logger("module/pruner") +func ConstructModule(tp node.Type, cfg *Config) fx.Option { prunerService := fx.Options( fx.Provide(fx.Annotate( newPrunerService, @@ -36,76 +35,102 @@ func ConstructModule(tp node.Type, cfg *Config) fx.Option { fx.Invoke(func(_ *pruner.Service) {}), ) + baseComponents := fx.Options( + fx.Supply(cfg), + // TODO @renaynay: move this to share module construction + fx.Supply(modshare.Window(availability.StorageWindow)), + advertiseArchival(tp, cfg), + prunerService, + ) + switch tp { case node.Light: - if cfg.EnableService { - return fx.Module("prune", - baseComponents, - prunerService, - fx.Provide(light.NewPruner), - ) - } - // We do not trigger DetectPreviousRun for Light nodes, to allow them to disable pruning at wish. - // They are not expected to store a samples outside the sampling window and so partially pruned is - // not a concern. + // LNs enforce pruning by default return fx.Module("prune", baseComponents, + // TODO(@walldiss @renaynay): remove conversion after Availability and Pruner interfaces are merged + // note this provide exists in pruner module to avoid cyclical imports + fx.Provide(func(la *light.ShareAvailability) pruner.Pruner { return la }), ) case node.Full: - if cfg.EnableService { - return fx.Module("prune", - baseComponents, - prunerService, - fxutil.ProvideAs(full.NewPruner, new(pruner.Pruner)), - ) + fullAvailOpts := make([]fullavail.Option, 0) + + if !cfg.EnableService { + // populate archival mode opts + fullAvailOpts = []fullavail.Option{fullavail.WithArchivalMode()} } + return fx.Module("prune", baseComponents, - fx.Invoke(func(ctx context.Context, ds datastore.Batching) error { - return pruner.DetectPreviousRun(ctx, ds) - }), + fx.Supply(fullAvailOpts), + fx.Provide(func(fa *fullavail.ShareAvailability) pruner.Pruner { return fa }), + convertToPruned(), ) case node.Bridge: - if cfg.EnableService { - return fx.Module("prune", - baseComponents, - prunerService, - fxutil.ProvideAs(full.NewPruner, new(pruner.Pruner)), - fx.Provide(func(window pruner.AvailabilityWindow) []core.Option { - return []core.Option{core.WithAvailabilityWindow(window)} - }), - ) + coreOpts := make([]core.Option, 0) + fullAvailOpts := make([]fullavail.Option, 0) + + if !cfg.EnableService { + // populate archival mode opts + coreOpts = []core.Option{core.WithArchivalMode()} + fullAvailOpts = []fullavail.Option{fullavail.WithArchivalMode()} } + return fx.Module("prune", baseComponents, - fx.Invoke(func(ctx context.Context, ds datastore.Batching) error { - return pruner.DetectPreviousRun(ctx, ds) - }), - fx.Provide(func() []core.Option { - return []core.Option{} - }), + fx.Provide(func(fa *fullavail.ShareAvailability) pruner.Pruner { return fa }), + fx.Supply(coreOpts), + fx.Supply(fullAvailOpts), + convertToPruned(), ) default: panic("unknown node type") } } -func availWindow(tp node.Type, pruneEnabled bool) fx.Option { - switch tp { - case node.Light: - // light nodes are still subject to sampling within window - // even if pruning is not enabled. - return fx.Provide(func() pruner.AvailabilityWindow { - return light.Window - }) - case node.Full, node.Bridge: - return fx.Provide(func() pruner.AvailabilityWindow { - if pruneEnabled { - return full.Window - } - return archival.Window - }) - default: - panic("unknown node type") +func advertiseArchival(tp node.Type, pruneCfg *Config) fx.Option { + if (tp == node.Full || tp == node.Bridge) && !pruneCfg.EnableService { + return fx.Supply(discovery.WithAdvertise()) } + return fx.Provide(func() discovery.Option { + var opt discovery.Option + return opt + }) +} + +// convertToPruned checks if the node is being converted to an archival node +// to a pruned node. +func convertToPruned() fx.Option { + return fx.Invoke(func( + ctx context.Context, + cfg *Config, + ds datastore.Batching, + p *pruner.Service, + ) error { + lastPrunedHeight, err := p.LastPruned(ctx) + if err != nil { + return err + } + + err = detectFirstRun(ctx, cfg, ds, lastPrunedHeight) + if err != nil { + return err + } + + isArchival := !cfg.EnableService + convert, err := fullavail.ConvertFromArchivalToPruned(ctx, ds, isArchival) + if err != nil { + return err + } + + // if we convert the node from archival to pruned, we need to reset the checkpoint + // to ensure the node goes back and deletes *all* blocks older than the + // availability window, as archival "pruning" only trims the .q4 file, + // but retains the ODS. + if convert { + return p.ResetCheckpoint(ctx) + } + + return nil + }) } diff --git a/nodebuilder/share/bitswap.go b/nodebuilder/share/bitswap.go new file mode 100644 index 0000000000..9316075dfe --- /dev/null +++ b/nodebuilder/share/bitswap.go @@ -0,0 +1,93 @@ +package share + +import ( + "context" + "fmt" + + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange" + "github.com/ipfs/go-datastore" + ipfsmetrics "github.com/ipfs/go-metrics-interface" + ipfsprom "github.com/ipfs/go-metrics-prometheus" + hst "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/fx" + + "github.com/celestiaorg/celestia-node/nodebuilder/node" + "github.com/celestiaorg/celestia-node/nodebuilder/p2p" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/bitswap" + "github.com/celestiaorg/celestia-node/store" +) + +// dataExchange constructs Exchange(Bitswap Composition) for Shwap +func dataExchange(tp node.Type, params bitSwapParams) exchange.SessionExchange { + prefix := protocolID(params.Net) + net := bitswap.NewNetwork(params.Host, prefix) + + if params.PromReg != nil { + // metrics scope is required for prometheus metrics and will be used as metrics name prefix + params.Ctx = ipfsmetrics.CtxScope(params.Ctx, "bitswap") + err := ipfsprom.Inject() + if err != nil { + return nil + } + } + + switch tp { + case node.Full, node.Bridge: + bs := bitswap.New(params.Ctx, net, params.Bs) + net.Start(bs.Client, bs.Server) + params.Lifecycle.Append(fx.Hook{ + OnStop: func(_ context.Context) (err error) { + net.Stop() + return bs.Close() + }, + }) + return bs + case node.Light: + cl := bitswap.NewClient(params.Ctx, net, params.Bs) + net.Start(cl) + params.Lifecycle.Append(fx.Hook{ + OnStop: func(_ context.Context) (err error) { + net.Stop() + return cl.Close() + }, + }) + return cl + default: + panic(fmt.Sprintf("unsupported node type: %v", tp)) + } +} + +func blockstoreFromDatastore(ds datastore.Batching) (blockstore.Blockstore, error) { + return blockstore.NewBlockstore(ds), nil +} + +func blockstoreFromEDSStore(store *store.Store, blockStoreCacheSize int) (blockstore.Blockstore, error) { + if blockStoreCacheSize == 0 { + // no cache, return plain blockstore + return &bitswap.Blockstore{Getter: store}, nil + } + withCache, err := store.WithCache("blockstore", blockStoreCacheSize) + if err != nil { + return nil, fmt.Errorf("create cached store for blockstore:%w", err) + } + bs := &bitswap.Blockstore{Getter: withCache} + return bs, nil +} + +type bitSwapParams struct { + fx.In + + Lifecycle fx.Lifecycle + Ctx context.Context + Net p2p.Network + Host hst.Host + Bs blockstore.Blockstore + PromReg prometheus.Registerer `optional:"true"` +} + +func protocolID(network p2p.Network) protocol.ID { + return protocol.ID(fmt.Sprintf("/celestia/%s", network)) +} diff --git a/nodebuilder/share/cmd/share.go b/nodebuilder/share/cmd/share.go index 58d6befecb..8f4181d5d9 100644 --- a/nodebuilder/share/cmd/share.go +++ b/nodebuilder/share/cmd/share.go @@ -1,17 +1,14 @@ package cmd import ( - "context" "encoding/hex" - "fmt" "strconv" "github.com/spf13/cobra" - rpc "github.com/celestiaorg/celestia-node/api/rpc/client" + libshare "github.com/celestiaorg/go-square/v2/share" + cmdnode "github.com/celestiaorg/celestia-node/cmd" - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/share" ) func init() { @@ -32,7 +29,7 @@ var Cmd = &cobra.Command{ var sharesAvailableCmd = &cobra.Command{ Use: "available", - Short: "Subjectively validates if Shares committed to the given Root are available on the Network.", + Short: "Subjectively validates if Shares committed to the given EDS are available on the Network.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client, err := cmdnode.ParseClientFromCtx(cmd.Context()) @@ -41,12 +38,12 @@ var sharesAvailableCmd = &cobra.Command{ } defer client.Close() - eh, err := getExtendedHeaderFromCmdArg(cmd.Context(), client, args[0]) + height, err := strconv.ParseUint(args[0], 10, 64) if err != nil { return err } - err = client.Share.SharesAvailable(cmd.Context(), eh) + err = client.Share.SharesAvailable(cmd.Context(), height) formatter := func(data interface{}) interface{} { err, ok := data.(error) available := false @@ -78,7 +75,7 @@ var getSharesByNamespaceCmd = &cobra.Command{ } defer client.Close() - eh, err := getExtendedHeaderFromCmdArg(cmd.Context(), client, args[0]) + height, err := strconv.ParseUint(args[0], 10, 64) if err != nil { return err } @@ -88,7 +85,7 @@ var getSharesByNamespaceCmd = &cobra.Command{ return err } - shares, err := client.Share.GetSharesByNamespace(cmd.Context(), eh, ns) + shares, err := client.Share.GetNamespaceData(cmd.Context(), height, ns) return cmdnode.PrintOutput(shares, err, nil) }, } @@ -104,7 +101,7 @@ var getShare = &cobra.Command{ } defer client.Close() - eh, err := getExtendedHeaderFromCmdArg(cmd.Context(), client, args[0]) + height, err := strconv.ParseUint(args[0], 10, 64) if err != nil { return err } @@ -119,22 +116,22 @@ var getShare = &cobra.Command{ return err } - s, err := client.Share.GetShare(cmd.Context(), eh, int(row), int(col)) + s, err := client.Share.GetShare(cmd.Context(), height, int(row), int(col)) formatter := func(data interface{}) interface{} { - sh, ok := data.(share.Share) + sh, ok := data.(libshare.Share) if !ok { return data } - ns := hex.EncodeToString(share.GetNamespace(sh)) + ns := hex.EncodeToString(sh.Namespace().Bytes()) return struct { Namespace string `json:"namespace"` Data []byte `json:"data"` }{ Namespace: ns, - Data: share.GetData(sh), + Data: sh.RawData(), } } return cmdnode.PrintOutput(s, err, formatter) @@ -152,26 +149,12 @@ var getEDS = &cobra.Command{ } defer client.Close() - eh, err := getExtendedHeaderFromCmdArg(cmd.Context(), client, args[0]) + height, err := strconv.ParseUint(args[0], 10, 64) if err != nil { return err } - shares, err := client.Share.GetEDS(cmd.Context(), eh) + shares, err := client.Share.GetEDS(cmd.Context(), height) return cmdnode.PrintOutput(shares, err, nil) }, } - -func getExtendedHeaderFromCmdArg(ctx context.Context, client *rpc.Client, arg string) (*header.ExtendedHeader, error) { - height, err := strconv.ParseUint(arg, 10, 64) - if err == nil { - return client.Header.GetByHeight(ctx, height) - } - - hash, err := hex.DecodeString(arg) - if err != nil { - return nil, fmt.Errorf("can't parse the height/hash argument: %w", err) - } - - return client.Header.GetByHash(ctx, hash) -} diff --git a/nodebuilder/share/config.go b/nodebuilder/share/config.go index 1d984b6dca..31f2eda5a3 100644 --- a/nodebuilder/share/config.go +++ b/nodebuilder/share/config.go @@ -5,17 +5,21 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/share/availability/light" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/p2p/discovery" - "github.com/celestiaorg/celestia-node/share/p2p/peers" - "github.com/celestiaorg/celestia-node/share/p2p/shrexeds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexnd" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/discovery" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/peers" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexeds" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexnd" + "github.com/celestiaorg/celestia-node/store" +) + +const ( + defaultBlockstoreCacheSize = 128 ) -// TODO: some params are pointers and other are not, Let's fix this. type Config struct { // EDSStoreParams sets eds store configuration parameters - EDSStoreParams *eds.Parameters + EDSStoreParams *store.Parameters + BlockStoreCacheSize uint UseShareExchange bool // ShrExEDSParams sets shrexeds client and server configuration parameters @@ -23,20 +27,21 @@ type Config struct { // ShrExNDParams sets shrexnd client and server configuration parameters ShrExNDParams *shrexnd.Parameters // PeerManagerParams sets peer-manager configuration parameters - PeerManagerParams peers.Parameters + PeerManagerParams *peers.Parameters - LightAvailability light.Parameters `toml:",omitempty"` + LightAvailability *light.Parameters `toml:",omitempty"` Discovery *discovery.Parameters } func DefaultConfig(tp node.Type) Config { cfg := Config{ - EDSStoreParams: eds.DefaultParameters(), - Discovery: discovery.DefaultParameters(), - ShrExEDSParams: shrexeds.DefaultParameters(), - ShrExNDParams: shrexnd.DefaultParameters(), - UseShareExchange: true, - PeerManagerParams: peers.DefaultParameters(), + EDSStoreParams: store.DefaultParameters(), + BlockStoreCacheSize: defaultBlockstoreCacheSize, + Discovery: discovery.DefaultParameters(), + ShrExEDSParams: shrexeds.DefaultParameters(), + ShrExNDParams: shrexnd.DefaultParameters(), + UseShareExchange: true, + PeerManagerParams: peers.DefaultParameters(), } if tp == node.Light { @@ -55,20 +60,23 @@ func (cfg *Config) Validate(tp node.Type) error { } if err := cfg.Discovery.Validate(); err != nil { - return fmt.Errorf("nodebuilder/share: %w", err) + return fmt.Errorf("discovery: %w", err) } if err := cfg.ShrExNDParams.Validate(); err != nil { - return fmt.Errorf("nodebuilder/share: %w", err) + return fmt.Errorf("shrexnd: %w", err) } if err := cfg.ShrExEDSParams.Validate(); err != nil { - return fmt.Errorf("nodebuilder/share: %w", err) + return fmt.Errorf("shrexeds: %w", err) } if err := cfg.PeerManagerParams.Validate(); err != nil { - return fmt.Errorf("nodebuilder/share: %w", err) + return fmt.Errorf("peer manager: %w", err) } + if err := cfg.EDSStoreParams.Validate(); err != nil { + return fmt.Errorf("eds store: %w", err) + } return nil } diff --git a/nodebuilder/share/constructors.go b/nodebuilder/share/constructors.go index 49789a8771..0efa6efa19 100644 --- a/nodebuilder/share/constructors.go +++ b/nodebuilder/share/constructors.go @@ -1,90 +1,61 @@ package share import ( - "context" - "errors" - - "github.com/filecoin-project/dagstore" - "github.com/ipfs/boxo/blockservice" - - "github.com/celestiaorg/celestia-app/v2/pkg/da" + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange" + "go.uber.org/fx" headerServ "github.com/celestiaorg/celestia-node/nodebuilder/header" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/getters" - "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/getters" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/bitswap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrex_getter" + "github.com/celestiaorg/celestia-node/store" ) -func newShareModule(getter share.Getter, avail share.Availability, header headerServ.Module) Module { +func newShareModule(getter shwap.Getter, avail share.Availability, header headerServ.Module) Module { return &module{getter, avail, header} } -// ensureEmptyCARExists adds an empty EDS to the provided EDS store. -func ensureEmptyCARExists(ctx context.Context, store *eds.Store) error { - emptyEDS := share.EmptyExtendedDataSquare() - emptyDAH, err := da.NewDataAvailabilityHeader(emptyEDS) - if err != nil { - return err - } - - err = store.Put(ctx, emptyDAH.Hash(), emptyEDS) - if errors.Is(err, dagstore.ErrShardExists) { - return nil - } - return err -} - -// ensureEmptyEDSInBS checks if the given DAG contains an empty block data square. -// If it does not, it stores an empty block. This optimization exists to prevent -// redundant storing of empty block data so that it is only stored once and returned -// upon request for a block with an empty data square. -func ensureEmptyEDSInBS(ctx context.Context, bServ blockservice.BlockService) error { - _, err := ipld.AddShares(ctx, share.EmptyBlockShares(), bServ) - return err +func bitswapGetter( + lc fx.Lifecycle, + exchange exchange.SessionExchange, + bstore blockstore.Blockstore, + wndw Window, +) *bitswap.Getter { + getter := bitswap.NewGetter(exchange, bstore, wndw.Duration()) + lc.Append(fx.StartStopHook(getter.Start, getter.Stop)) + return getter } func lightGetter( - shrexGetter *getters.ShrexGetter, - ipldGetter *getters.IPLDGetter, + shrexGetter *shrex_getter.Getter, + bitswapGetter *bitswap.Getter, cfg Config, -) share.Getter { - var cascade []share.Getter - if cfg.UseShareExchange { - cascade = append(cascade, shrexGetter) - } - cascade = append(cascade, ipldGetter) - return getters.NewCascadeGetter(cascade) -} - -// ShrexGetter is added to bridge nodes for the case that a shard is removed -// after detected shard corruption. This ensures the block is fetched and stored -// by shrex the next time the data is retrieved (meaning shard recovery is -// manual after corruption is detected). -func bridgeGetter( - storeGetter *getters.StoreGetter, - shrexGetter *getters.ShrexGetter, - cfg Config, -) share.Getter { - var cascade []share.Getter - cascade = append(cascade, storeGetter) +) shwap.Getter { + var cascade []shwap.Getter if cfg.UseShareExchange { cascade = append(cascade, shrexGetter) } + cascade = append(cascade, bitswapGetter) return getters.NewCascadeGetter(cascade) } -func fullGetter( - storeGetter *getters.StoreGetter, - shrexGetter *getters.ShrexGetter, - ipldGetter *getters.IPLDGetter, +// Getter is added to bridge nodes for the case where Bridge nodes are +// running in a pruned mode. This ensures the block can be retrieved from +// the network if it was pruned from the local store. +func bridgeAndFullGetter( + storeGetter *store.Getter, + shrexGetter *shrex_getter.Getter, + bitswapGetter *bitswap.Getter, cfg Config, -) share.Getter { - var cascade []share.Getter +) shwap.Getter { + var cascade []shwap.Getter cascade = append(cascade, storeGetter) if cfg.UseShareExchange { cascade = append(cascade, shrexGetter) } - cascade = append(cascade, ipldGetter) + cascade = append(cascade, bitswapGetter) return getters.NewCascadeGetter(cascade) } diff --git a/nodebuilder/share/mocks/api.go b/nodebuilder/share/mocks/api.go index c24a5dc771..d392f39f5b 100644 --- a/nodebuilder/share/mocks/api.go +++ b/nodebuilder/share/mocks/api.go @@ -9,10 +9,11 @@ import ( reflect "reflect" header "github.com/celestiaorg/celestia-node/header" - share "github.com/celestiaorg/celestia-node/share" + share "github.com/celestiaorg/celestia-node/nodebuilder/share" + shwap "github.com/celestiaorg/celestia-node/share/shwap" + share0 "github.com/celestiaorg/go-square/v2/share" rsmt2d "github.com/celestiaorg/rsmt2d" gomock "github.com/golang/mock/gomock" - types "github.com/tendermint/tendermint/types" ) // MockModule is a mock of Module interface. @@ -39,7 +40,7 @@ func (m *MockModule) EXPECT() *MockModuleMockRecorder { } // GetEDS mocks base method. -func (m *MockModule) GetEDS(arg0 context.Context, arg1 *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { +func (m *MockModule) GetEDS(arg0 context.Context, arg1 uint64) (*rsmt2d.ExtendedDataSquare, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEDS", arg0, arg1) ret0, _ := ret[0].(*rsmt2d.ExtendedDataSquare) @@ -53,14 +54,28 @@ func (mr *MockModuleMockRecorder) GetEDS(arg0, arg1 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEDS", reflect.TypeOf((*MockModule)(nil).GetEDS), arg0, arg1) } +// GetNamespaceData mocks base method. +func (m *MockModule) GetNamespaceData(arg0 context.Context, arg1 uint64, arg2 share0.Namespace) (shwap.NamespaceData, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNamespaceData", arg0, arg1, arg2) + ret0, _ := ret[0].(shwap.NamespaceData) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNamespaceData indicates an expected call of GetNamespaceData. +func (mr *MockModuleMockRecorder) GetNamespaceData(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNamespaceData", reflect.TypeOf((*MockModule)(nil).GetNamespaceData), arg0, arg1, arg2) +} + // GetRange mocks base method. -func (m *MockModule) GetRange(arg0 context.Context, arg1 uint64, arg2, arg3 int) ([][]byte, *types.ShareProof, error) { +func (m *MockModule) GetRange(arg0 context.Context, arg1 uint64, arg2, arg3 int) (*share.GetRangeResult, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRange", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([][]byte) - ret1, _ := ret[1].(*types.ShareProof) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret0, _ := ret[0].(*share.GetRangeResult) + ret1, _ := ret[1].(error) + return ret0, ret1 } // GetRange indicates an expected call of GetRange. @@ -69,38 +84,53 @@ func (mr *MockModuleMockRecorder) GetRange(arg0, arg1, arg2, arg3 interface{}) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRange", reflect.TypeOf((*MockModule)(nil).GetRange), arg0, arg1, arg2, arg3) } -// GetShare mocks base method. -func (m *MockModule) GetShare(arg0 context.Context, arg1 *header.ExtendedHeader, arg2, arg3 int) ([]byte, error) { +// GetRow mocks base method. +func (m *MockModule) GetRow(arg0 context.Context, arg1 uint64, arg2 int) (shwap.Row, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetShare", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]byte) + ret := m.ctrl.Call(m, "GetRow", arg0, arg1, arg2) + ret0, _ := ret[0].(shwap.Row) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetShare indicates an expected call of GetShare. -func (mr *MockModuleMockRecorder) GetShare(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// GetRow indicates an expected call of GetRow. +func (mr *MockModuleMockRecorder) GetRow(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShare", reflect.TypeOf((*MockModule)(nil).GetShare), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRow", reflect.TypeOf((*MockModule)(nil).GetRow), arg0, arg1, arg2) +} + +// GetSamples mocks base method. +func (m *MockModule) GetSamples(arg0 context.Context, arg1 *header.ExtendedHeader, arg2 []shwap.SampleCoords) ([]shwap.Sample, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSamples", arg0, arg1, arg2) + ret0, _ := ret[0].([]shwap.Sample) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSamples indicates an expected call of GetSamples. +func (mr *MockModuleMockRecorder) GetSamples(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSamples", reflect.TypeOf((*MockModule)(nil).GetSamples), arg0, arg1, arg2) } -// GetSharesByNamespace mocks base method. -func (m *MockModule) GetSharesByNamespace(arg0 context.Context, arg1 *header.ExtendedHeader, arg2 share.Namespace) (share.NamespacedShares, error) { +// GetShare mocks base method. +func (m *MockModule) GetShare(arg0 context.Context, arg1 uint64, arg2, arg3 int) (share0.Share, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSharesByNamespace", arg0, arg1, arg2) - ret0, _ := ret[0].(share.NamespacedShares) + ret := m.ctrl.Call(m, "GetShare", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(share0.Share) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetSharesByNamespace indicates an expected call of GetSharesByNamespace. -func (mr *MockModuleMockRecorder) GetSharesByNamespace(arg0, arg1, arg2 interface{}) *gomock.Call { +// GetShare indicates an expected call of GetShare. +func (mr *MockModuleMockRecorder) GetShare(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSharesByNamespace", reflect.TypeOf((*MockModule)(nil).GetSharesByNamespace), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShare", reflect.TypeOf((*MockModule)(nil).GetShare), arg0, arg1, arg2, arg3) } // SharesAvailable mocks base method. -func (m *MockModule) SharesAvailable(arg0 context.Context, arg1 *header.ExtendedHeader) error { +func (m *MockModule) SharesAvailable(arg0 context.Context, arg1 uint64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SharesAvailable", arg0, arg1) ret0, _ := ret[0].(error) diff --git a/nodebuilder/share/module.go b/nodebuilder/share/module.go index 5d0dbbd096..3becb947c8 100644 --- a/nodebuilder/share/module.go +++ b/nodebuilder/share/module.go @@ -2,61 +2,57 @@ package share import ( "context" + "fmt" + "github.com/ipfs/boxo/blockstore" "github.com/ipfs/go-datastore" "github.com/libp2p/go-libp2p/core/host" "go.uber.org/fx" "github.com/celestiaorg/celestia-node/nodebuilder/node" modp2p "github.com/celestiaorg/celestia-node/nodebuilder/p2p" - lightprune "github.com/celestiaorg/celestia-node/pruner/light" "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/availability" "github.com/celestiaorg/celestia-node/share/availability/full" "github.com/celestiaorg/celestia-node/share/availability/light" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/getters" - "github.com/celestiaorg/celestia-node/share/p2p/peers" - "github.com/celestiaorg/celestia-node/share/p2p/shrexeds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexnd" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/peers" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrex_getter" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexeds" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexnd" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" + "github.com/celestiaorg/celestia-node/store" ) func ConstructModule(tp node.Type, cfg *Config, options ...fx.Option) fx.Option { // sanitize config values before constructing module - cfgErr := cfg.Validate(tp) + err := cfg.Validate(tp) + if err != nil { + return fx.Error(fmt.Errorf("nodebuilder/share: validate config: %w", err)) + } baseComponents := fx.Options( fx.Supply(*cfg), - fx.Error(cfgErr), fx.Options(options...), fx.Provide(newShareModule), availabilityComponents(tp, cfg), shrexComponents(tp, cfg), - peerComponents(tp, cfg), + bitswapComponents(tp, cfg), + peerManagementComponents(tp, cfg), ) switch tp { - case node.Bridge: - return fx.Module( - "share", - baseComponents, - edsStoreComponents(cfg), - fx.Provide(bridgeGetter), - ) - case node.Full: + case node.Bridge, node.Full: return fx.Module( "share", baseComponents, edsStoreComponents(cfg), - fx.Provide(getters.NewIPLDGetter), - fx.Provide(fullGetter), + fx.Provide(bridgeAndFullGetter), ) case node.Light: return fx.Module( "share", baseComponents, - fx.Invoke(ensureEmptyEDSInBS), - fx.Provide(getters.NewIPLDGetter), fx.Provide(lightGetter), ) default: @@ -64,6 +60,29 @@ func ConstructModule(tp node.Type, cfg *Config, options ...fx.Option) fx.Option } } +func bitswapComponents(tp node.Type, cfg *Config) fx.Option { + opts := fx.Options( + fx.Provide(dataExchange), + fx.Provide(bitswapGetter), + ) + switch tp { + case node.Light: + return fx.Options( + opts, + fx.Provide(blockstoreFromDatastore), + ) + case node.Full, node.Bridge: + return fx.Options( + opts, + fx.Provide(func(store *store.Store) (blockstore.Blockstore, error) { + return blockstoreFromEDSStore(store, int(cfg.BlockStoreCacheSize)) + }), + ) + default: + panic("invalid node type") + } +} + func shrexComponents(tp node.Type, cfg *Config) fx.Option { opts := fx.Options( fx.Provide( @@ -92,19 +111,19 @@ func shrexComponents(tp node.Type, cfg *Config) fx.Option { edsClient *shrexeds.Client, ndClient *shrexnd.Client, managers map[string]*peers.Manager, - ) *getters.ShrexGetter { - return getters.NewShrexGetter( + ) *shrex_getter.Getter { + return shrex_getter.NewGetter( edsClient, ndClient, managers[fullNodesTag], managers[archivalNodesTag], - lightprune.Window, + availability.RequestWindow, ) }, - fx.OnStart(func(ctx context.Context, getter *getters.ShrexGetter) error { + fx.OnStart(func(ctx context.Context, getter *shrex_getter.Getter) error { return getter.Start(ctx) }), - fx.OnStop(func(ctx context.Context, getter *getters.ShrexGetter) error { + fx.OnStop(func(ctx context.Context, getter *shrex_getter.Getter) error { return getter.Stop(ctx) }), )), @@ -125,7 +144,7 @@ func shrexComponents(tp node.Type, cfg *Config) fx.Option { return fx.Options( opts, shrexServerComponents(cfg), - fx.Provide(getters.NewStoreGetter), + fx.Provide(store.NewGetter), fx.Provide(func(shrexSub *shrexsub.PubSub) shrexsub.BroadcastFn { return shrexSub.Broadcast }), @@ -134,7 +153,7 @@ func shrexComponents(tp node.Type, cfg *Config) fx.Option { return fx.Options( opts, shrexServerComponents(cfg), - fx.Provide(getters.NewStoreGetter), + fx.Provide(store.NewGetter), fx.Provide(func(shrexSub *shrexsub.PubSub) shrexsub.BroadcastFn { return shrexSub.Broadcast }), @@ -155,7 +174,7 @@ func shrexServerComponents(cfg *Config) fx.Option { return fx.Options( fx.Invoke(func(_ *shrexeds.Server, _ *shrexnd.Server) {}), fx.Provide(fx.Annotate( - func(host host.Host, store *eds.Store, network modp2p.Network) (*shrexeds.Server, error) { + func(host host.Host, store *store.Store, network modp2p.Network) (*shrexeds.Server, error) { cfg.ShrExEDSParams.WithNetworkID(network.String()) return shrexeds.NewServer(cfg.ShrExEDSParams, host, store) }, @@ -169,7 +188,7 @@ func shrexServerComponents(cfg *Config) fx.Option { fx.Provide(fx.Annotate( func( host host.Host, - store *eds.Store, + store *store.Store, network modp2p.Network, ) (*shrexnd.Server, error) { cfg.ShrExNDParams.WithNetworkID(network.String()) @@ -188,17 +207,10 @@ func shrexServerComponents(cfg *Config) fx.Option { func edsStoreComponents(cfg *Config) fx.Option { return fx.Options( fx.Provide(fx.Annotate( - func(path node.StorePath, ds datastore.Batching) (*eds.Store, error) { - return eds.NewStore(cfg.EDSStoreParams, string(path), ds) + func(path node.StorePath) (*store.Store, error) { + return store.NewStore(cfg.EDSStoreParams, string(path)) }, - fx.OnStart(func(ctx context.Context, store *eds.Store) error { - err := store.Start(ctx) - if err != nil { - return err - } - return ensureEmptyCARExists(ctx, store) - }), - fx.OnStop(func(ctx context.Context, store *eds.Store) error { + fx.OnStop(func(ctx context.Context, store *store.Store) error { return store.Stop(ctx) }), )), @@ -210,24 +222,30 @@ func availabilityComponents(tp node.Type, cfg *Config) fx.Option { case node.Light: return fx.Options( fx.Provide(fx.Annotate( - func(getter share.Getter, ds datastore.Batching) *light.ShareAvailability { + func(getter shwap.Getter, ds datastore.Batching, bs blockstore.Blockstore) *light.ShareAvailability { return light.NewShareAvailability( getter, ds, + bs, light.WithSampleAmount(cfg.LightAvailability.SampleAmount), ) }, + fx.As(fx.Self()), + fx.As(new(share.Availability)), fx.OnStop(func(ctx context.Context, la *light.ShareAvailability) error { return la.Close(ctx) }), )), - fx.Provide(func(avail *light.ShareAvailability) share.Availability { - return avail - }), ) case node.Bridge, node.Full: return fx.Options( - fx.Provide(full.NewShareAvailability), + fx.Provide(func( + s *store.Store, + getter shwap.Getter, + opts []full.Option, + ) *full.ShareAvailability { + return full.NewShareAvailability(s, getter, opts...) + }), fx.Provide(func(avail *full.ShareAvailability) share.Availability { return avail }), diff --git a/nodebuilder/share/opts.go b/nodebuilder/share/opts.go index 9c122b7b0f..cfea26dbb4 100644 --- a/nodebuilder/share/opts.go +++ b/nodebuilder/share/opts.go @@ -3,12 +3,12 @@ package share import ( "errors" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/getters" - disc "github.com/celestiaorg/celestia-node/share/p2p/discovery" - "github.com/celestiaorg/celestia-node/share/p2p/peers" - "github.com/celestiaorg/celestia-node/share/p2p/shrexeds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexnd" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/discovery" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/peers" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrex_getter" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexeds" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexnd" + "github.com/celestiaorg/celestia-node/store" ) // WithPeerManagerMetrics is a utility function to turn on peer manager metrics and that is @@ -23,7 +23,7 @@ func WithPeerManagerMetrics(managers map[string]*peers.Manager) error { // WithDiscoveryMetrics is a utility function to turn on discovery metrics and that is expected to // be "invoked" by the fx lifecycle. -func WithDiscoveryMetrics(discs []*disc.Discovery) error { +func WithDiscoveryMetrics(discs []*discovery.Discovery) error { var err error for _, disc := range discs { err = errors.Join(err, disc.WithMetrics()) @@ -49,10 +49,10 @@ func WithShrexServerMetrics(edsServer *shrexeds.Server, ndServer *shrexnd.Server return ndServer.WithMetrics() } -func WithShrexGetterMetrics(sg *getters.ShrexGetter) error { +func WithShrexGetterMetrics(sg *shrex_getter.Getter) error { return sg.WithMetrics() } -func WithStoreMetrics(s *eds.Store) error { +func WithStoreMetrics(s *store.Store) error { return s.WithMetrics() } diff --git a/nodebuilder/share/p2p_constructors.go b/nodebuilder/share/p2p_constructors.go index aae1a325aa..d08d3891d3 100644 --- a/nodebuilder/share/p2p_constructors.go +++ b/nodebuilder/share/p2p_constructors.go @@ -1,8 +1,9 @@ package share import ( + dht "github.com/libp2p/go-libp2p-kad-dht" + p2pdisc "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/routing" routingdisc "github.com/libp2p/go-libp2p/p2p/discovery/routing" "github.com/libp2p/go-libp2p/p2p/net/conngater" "go.uber.org/fx" @@ -12,10 +13,9 @@ import ( "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/nodebuilder/node" - modprune "github.com/celestiaorg/celestia-node/nodebuilder/pruner" - disc "github.com/celestiaorg/celestia-node/share/p2p/discovery" - "github.com/celestiaorg/celestia-node/share/p2p/peers" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/discovery" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/peers" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) const ( @@ -24,13 +24,17 @@ const ( // archivalNodesTag is the tag used to identify archival nodes in the // discovery service. archivalNodesTag = "archival" + + // protocolVersion is a prefix for all tags used in discovery. It is bumped when + // there are protocol breaking changes to prevent new software version to discover older versions. + protocolVersion = "v0.1.0" ) -// TODO @renaynay: rename -func peerComponents(tp node.Type, cfg *Config) fx.Option { +func peerManagementComponents(tp node.Type, cfg *Config) fx.Option { return fx.Options( + fx.Provide(routingDiscovery), fullDiscoveryAndPeerManager(tp, cfg), - archivalDiscoveryAndPeerManager(tp, cfg), + archivalDiscoveryAndPeerManager(cfg), ) } @@ -42,14 +46,14 @@ func fullDiscoveryAndPeerManager(tp node.Type, cfg *Config) fx.Option { func( lc fx.Lifecycle, host host.Host, - r routing.ContentRouting, connGater *conngater.BasicConnectionGater, + disc p2pdisc.Discovery, shrexSub *shrexsub.PubSub, headerSub libhead.Subscriber[*header.ExtendedHeader], // we must ensure Syncer is started before PeerManager // so that Syncer registers header validator before PeerManager subscribes to headers _ *sync.Syncer[*header.ExtendedHeader], - ) (*peers.Manager, *disc.Discovery, error) { + ) (*peers.Manager, *discovery.Discovery, error) { var managerOpts []peers.Option if tp != node.Bridge { // BNs do not need the overhead of shrexsub peer pools as @@ -58,7 +62,7 @@ func fullDiscoveryAndPeerManager(tp node.Type, cfg *Config) fx.Option { } fullManager, err := peers.NewManager( - cfg.PeerManagerParams, + *cfg.PeerManagerParams, host, connGater, fullNodesTag, @@ -68,18 +72,19 @@ func fullDiscoveryAndPeerManager(tp node.Type, cfg *Config) fx.Option { return nil, nil, err } - discOpts := []disc.Option{disc.WithOnPeersUpdate(fullManager.UpdateNodePool)} + discOpts := []discovery.Option{discovery.WithOnPeersUpdate(fullManager.UpdateNodePool)} if tp != node.Light { // only FN and BNs should advertise to `full` topic - discOpts = append(discOpts, disc.WithAdvertise()) + discOpts = append(discOpts, discovery.WithAdvertise()) } - fullDisc, err := disc.NewDiscovery( + fullDisc, err := discovery.NewDiscovery( cfg.Discovery, host, - routingdisc.NewRoutingDiscovery(r), + disc, fullNodesTag, + protocolVersion, discOpts..., ) if err != nil { @@ -94,20 +99,21 @@ func fullDiscoveryAndPeerManager(tp node.Type, cfg *Config) fx.Option { }) } -// archivalDiscoveryAndPeerManager TODO @renaynay -func archivalDiscoveryAndPeerManager(tp node.Type, cfg *Config) fx.Option { +// archivalDiscoveryAndPeerManager builds the discovery instance and peer manager +// for discovering and managing peers advertising on the `archival` tag +func archivalDiscoveryAndPeerManager(cfg *Config) fx.Option { return fx.Provide( func( lc fx.Lifecycle, - pruneCfg *modprune.Config, - d *disc.Discovery, - manager *peers.Manager, + fullDisc *discovery.Discovery, + fullManager *peers.Manager, h host.Host, - r routing.ContentRouting, + disc p2pdisc.Discovery, gater *conngater.BasicConnectionGater, - ) (map[string]*peers.Manager, []*disc.Discovery, error) { + discOpt discovery.Option, + ) (map[string]*peers.Manager, []*discovery.Discovery, error) { archivalPeerManager, err := peers.NewManager( - cfg.PeerManagerParams, + *cfg.PeerManagerParams, h, gater, archivalNodesTag, @@ -116,17 +122,17 @@ func archivalDiscoveryAndPeerManager(tp node.Type, cfg *Config) fx.Option { return nil, nil, err } - discOpts := []disc.Option{disc.WithOnPeersUpdate(archivalPeerManager.UpdateNodePool)} - - if (tp == node.Bridge || tp == node.Full) && !pruneCfg.EnableService { - discOpts = append(discOpts, disc.WithAdvertise()) + discOpts := []discovery.Option{discovery.WithOnPeersUpdate(archivalPeerManager.UpdateNodePool)} + if discOpt != nil { + discOpts = append(discOpts, discOpt) } - archivalDisc, err := disc.NewDiscovery( + archivalDisc, err := discovery.NewDiscovery( cfg.Discovery, h, - routingdisc.NewRoutingDiscovery(r), + disc, archivalNodesTag, + protocolVersion, discOpts..., ) if err != nil { @@ -137,7 +143,11 @@ func archivalDiscoveryAndPeerManager(tp node.Type, cfg *Config) fx.Option { OnStop: archivalDisc.Stop, }) - managers := map[string]*peers.Manager{fullNodesTag: manager, archivalNodesTag: archivalPeerManager} - return managers, []*disc.Discovery{d, archivalDisc}, nil + managers := map[string]*peers.Manager{fullNodesTag: fullManager, archivalNodesTag: archivalPeerManager} + return managers, []*discovery.Discovery{fullDisc, archivalDisc}, nil }) } + +func routingDiscovery(dht *dht.IpfsDHT) p2pdisc.Discovery { + return routingdisc.NewRoutingDiscovery(dht) +} diff --git a/nodebuilder/share/share.go b/nodebuilder/share/share.go index a2cef51170..46df1e2379 100644 --- a/nodebuilder/share/share.go +++ b/nodebuilder/share/share.go @@ -5,12 +5,14 @@ import ( "github.com/tendermint/tendermint/types" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" headerServ "github.com/celestiaorg/celestia-node/nodebuilder/header" "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" ) var _ Module = (*API)(nil) @@ -18,14 +20,14 @@ var _ Module = (*API)(nil) // GetRangeResult wraps the return value of the GetRange endpoint // because Json-RPC doesn't support more than two return values. type GetRangeResult struct { - Shares []share.Share + Shares []libshare.Share Proof *types.ShareProof } // Module provides access to any data square or block share on the network. // // All Get methods provided on Module follow the following flow: -// 1. Check local storage for the requested Share. +// 1. Check local storage for the requested share. // 2. If exists // * Load from disk // * Return @@ -41,39 +43,52 @@ type GetRangeResult struct { type Module interface { // SharesAvailable subjectively validates if Shares committed to the given // ExtendedHeader are available on the Network. - SharesAvailable(context.Context, *header.ExtendedHeader) error + SharesAvailable(ctx context.Context, height uint64) error // GetShare gets a Share by coordinates in EDS. - GetShare(ctx context.Context, header *header.ExtendedHeader, row, col int) (share.Share, error) + GetShare(ctx context.Context, height uint64, row, col int) (libshare.Share, error) + // GetSamples gets sample for given indices. + GetSamples(ctx context.Context, header *header.ExtendedHeader, indices []shwap.SampleCoords) ([]shwap.Sample, error) // GetEDS gets the full EDS identified by the given extended header. - GetEDS(ctx context.Context, header *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) - // GetSharesByNamespace gets all shares from an EDS within the given namespace. + GetEDS(ctx context.Context, height uint64) (*rsmt2d.ExtendedDataSquare, error) + // GetRow gets all shares from specified row. + GetRow(context.Context, uint64, int) (shwap.Row, error) + // GetNamespaceData gets all shares from an EDS within the given namespace. // Shares are returned in a row-by-row order if the namespace spans multiple rows. - GetSharesByNamespace( - ctx context.Context, header *header.ExtendedHeader, namespace share.Namespace, - ) (share.NamespacedShares, error) + GetNamespaceData( + ctx context.Context, height uint64, namespace libshare.Namespace, + ) (shwap.NamespaceData, error) // GetRange gets a list of shares and their corresponding proof. GetRange(ctx context.Context, height uint64, start, end int) (*GetRangeResult, error) } // API is a wrapper around Module for the RPC. -// TODO(@distractedm1nd): These structs need to be autogenerated. type API struct { Internal struct { - SharesAvailable func(context.Context, *header.ExtendedHeader) error `perm:"read"` + SharesAvailable func(ctx context.Context, height uint64) error `perm:"read"` GetShare func( ctx context.Context, - header *header.ExtendedHeader, + height uint64, row, col int, - ) (share.Share, error) `perm:"read"` - GetEDS func( + ) (libshare.Share, error) `perm:"read"` + GetSamples func( ctx context.Context, header *header.ExtendedHeader, + indices []shwap.SampleCoords, + ) ([]shwap.Sample, error) `perm:"read"` + GetEDS func( + ctx context.Context, + height uint64, ) (*rsmt2d.ExtendedDataSquare, error) `perm:"read"` - GetSharesByNamespace func( + GetRow func( + context.Context, + uint64, + int, + ) (shwap.Row, error) `perm:"read"` + GetNamespaceData func( ctx context.Context, - header *header.ExtendedHeader, - namespace share.Namespace, - ) (share.NamespacedShares, error) `perm:"read"` + height uint64, + namespace libshare.Namespace, + ) (shwap.NamespaceData, error) `perm:"read"` GetRange func( ctx context.Context, height uint64, @@ -82,55 +97,121 @@ type API struct { } } -func (api *API) SharesAvailable(ctx context.Context, header *header.ExtendedHeader) error { - return api.Internal.SharesAvailable(ctx, header) +func (api *API) SharesAvailable(ctx context.Context, height uint64) error { + return api.Internal.SharesAvailable(ctx, height) +} + +func (api *API) GetShare(ctx context.Context, height uint64, row, col int) (libshare.Share, error) { + return api.Internal.GetShare(ctx, height, row, col) +} + +func (api *API) GetSamples(ctx context.Context, header *header.ExtendedHeader, + indices []shwap.SampleCoords, +) ([]shwap.Sample, error) { + return api.Internal.GetSamples(ctx, header, indices) } -func (api *API) GetShare(ctx context.Context, header *header.ExtendedHeader, row, col int) (share.Share, error) { - return api.Internal.GetShare(ctx, header, row, col) +func (api *API) GetEDS(ctx context.Context, height uint64) (*rsmt2d.ExtendedDataSquare, error) { + return api.Internal.GetEDS(ctx, height) } -func (api *API) GetEDS(ctx context.Context, header *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { - return api.Internal.GetEDS(ctx, header) +func (api *API) GetRow(ctx context.Context, height uint64, rowIdx int) (shwap.Row, error) { + return api.Internal.GetRow(ctx, height, rowIdx) } func (api *API) GetRange(ctx context.Context, height uint64, start, end int) (*GetRangeResult, error) { return api.Internal.GetRange(ctx, height, start, end) } -func (api *API) GetSharesByNamespace( +func (api *API) GetNamespaceData( ctx context.Context, - header *header.ExtendedHeader, - namespace share.Namespace, -) (share.NamespacedShares, error) { - return api.Internal.GetSharesByNamespace(ctx, header, namespace) + height uint64, + namespace libshare.Namespace, +) (shwap.NamespaceData, error) { + return api.Internal.GetNamespaceData(ctx, height, namespace) } type module struct { - share.Getter - share.Availability - hs headerServ.Module + getter shwap.Getter + avail share.Availability + hs headerServ.Module } -func (m module) SharesAvailable(ctx context.Context, header *header.ExtendedHeader) error { - return m.Availability.SharesAvailable(ctx, header) +func (m module) GetShare(ctx context.Context, height uint64, row, col int) (libshare.Share, error) { + header, err := m.hs.GetByHeight(ctx, height) + if err != nil { + return libshare.Share{}, err + } + + idx := shwap.SampleCoords{Row: row, Col: col} + + smpls, err := m.getter.GetSamples(ctx, header, []shwap.SampleCoords{idx}) + if err != nil { + return libshare.Share{}, err + } + + return smpls[0].Share, nil } -func (m module) GetRange(ctx context.Context, height uint64, start, end int) (*GetRangeResult, error) { - extendedHeader, err := m.hs.GetByHeight(ctx, height) +func (m module) GetSamples(ctx context.Context, header *header.ExtendedHeader, + indices []shwap.SampleCoords, +) ([]shwap.Sample, error) { + return m.getter.GetSamples(ctx, header, indices) +} + +func (m module) GetEDS(ctx context.Context, height uint64) (*rsmt2d.ExtendedDataSquare, error) { + header, err := m.hs.GetByHeight(ctx, height) if err != nil { return nil, err } - extendedDataSquare, err := m.GetEDS(ctx, extendedHeader) + return m.getter.GetEDS(ctx, header) +} + +func (m module) SharesAvailable(ctx context.Context, height uint64) error { + header, err := m.hs.GetByHeight(ctx, height) + if err != nil { + return err + } + return m.avail.SharesAvailable(ctx, header) +} + +func (m module) GetRange(ctx context.Context, height uint64, start, end int) (*GetRangeResult, error) { + extendedDataSquare, err := m.GetEDS(ctx, height) if err != nil { return nil, err } + proof, err := eds.ProveShares(extendedDataSquare, start, end) if err != nil { return nil, err } + + shares, err := libshare.FromBytes(extendedDataSquare.FlattenedODS()[start:end]) + if err != nil { + return nil, err + } return &GetRangeResult{ - extendedDataSquare.FlattenedODS()[start:end], - proof, + Shares: shares, + Proof: proof, }, nil } + +func (m module) GetNamespaceData( + ctx context.Context, + height uint64, + namespace libshare.Namespace, +) (shwap.NamespaceData, error) { + header, err := m.hs.GetByHeight(ctx, height) + if err != nil { + return nil, err + } + return m.getter.GetNamespaceData(ctx, header, namespace) +} + +func (m module) GetRow(ctx context.Context, height uint64, rowIdx int) (shwap.Row, error) { + header, err := m.hs.GetByHeight(ctx, height) + if err != nil { + return shwap.Row{}, err + } + return m.getter.GetRow(ctx, header, rowIdx) +} diff --git a/nodebuilder/share/share_test.go b/nodebuilder/share/share_test.go deleted file mode 100644 index db170709db..0000000000 --- a/nodebuilder/share/share_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package share - -import ( - "context" - "testing" - - "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" -) - -func Test_EmptyCARExists(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - edsStore, err := eds.NewStore(eds.DefaultParameters(), t.TempDir(), ds) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - - eds := share.EmptyExtendedDataSquare() - dah, err := share.NewRoot(eds) - require.NoError(t, err) - - // add empty EDS to store - err = ensureEmptyCARExists(ctx, edsStore) - assert.NoError(t, err) - - // assert that the empty car exists - has, err := edsStore.Has(ctx, dah.Hash()) - assert.True(t, has) - assert.NoError(t, err) - - // assert that the empty car is, in fact, empty - emptyEds, err := edsStore.Get(ctx, dah.Hash()) - assert.Equal(t, eds.Flattened(), emptyEds.Flattened()) - assert.NoError(t, err) -} diff --git a/nodebuilder/share/window.go b/nodebuilder/share/window.go new file mode 100644 index 0000000000..0f41543caf --- /dev/null +++ b/nodebuilder/share/window.go @@ -0,0 +1,13 @@ +package share + +import ( + "time" +) + +// Window is a type alias for time.Duration used in nodebuilder +// to provide sampling windows to their relevant components +type Window time.Duration + +func (w Window) Duration() time.Duration { + return time.Duration(w) +} diff --git a/nodebuilder/state/core.go b/nodebuilder/state/core.go index 5a61f43345..39ab732368 100644 --- a/nodebuilder/state/core.go +++ b/nodebuilder/state/core.go @@ -9,6 +9,7 @@ import ( "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/nodebuilder/core" modfraud "github.com/celestiaorg/celestia-node/nodebuilder/fraud" + "github.com/celestiaorg/celestia-node/nodebuilder/p2p" "github.com/celestiaorg/celestia-node/share/eds/byzantine" "github.com/celestiaorg/celestia-node/state" ) @@ -21,6 +22,7 @@ func coreAccessor( keyname AccountName, sync *sync.Syncer[*header.ExtendedHeader], fraudServ libfraud.Service[*header.ExtendedHeader], + network p2p.Network, opts []state.Option, ) ( *state.CoreAccessor, @@ -28,7 +30,8 @@ func coreAccessor( *modfraud.ServiceBreaker[*state.CoreAccessor, *header.ExtendedHeader], error, ) { - ca, err := state.NewCoreAccessor(keyring, string(keyname), sync, corecfg.IP, corecfg.GRPCPort, opts...) + ca, err := state.NewCoreAccessor(keyring, string(keyname), sync, corecfg.IP, corecfg.GRPCPort, + network.String(), opts...) sBreaker := &modfraud.ServiceBreaker[*state.CoreAccessor, *header.ExtendedHeader]{ Service: ca, diff --git a/nodebuilder/state/mocks/api.go b/nodebuilder/state/mocks/api.go index a4fb8d8cec..59ba400b5e 100644 --- a/nodebuilder/state/mocks/api.go +++ b/nodebuilder/state/mocks/api.go @@ -10,10 +10,10 @@ import ( math "cosmossdk.io/math" state "github.com/celestiaorg/celestia-node/state" + share "github.com/celestiaorg/go-square/v2/share" types "github.com/cosmos/cosmos-sdk/types" types0 "github.com/cosmos/cosmos-sdk/x/staking/types" gomock "github.com/golang/mock/gomock" - types1 "github.com/tendermint/tendermint/proto/tendermint/types" ) // MockModule is a mock of Module interface. @@ -205,7 +205,7 @@ func (mr *MockModuleMockRecorder) RevokeGrantFee(arg0, arg1, arg2 interface{}) * } // SubmitPayForBlob mocks base method. -func (m *MockModule) SubmitPayForBlob(arg0 context.Context, arg1 []*types1.Blob, arg2 *state.TxConfig) (*types.TxResponse, error) { +func (m *MockModule) SubmitPayForBlob(arg0 context.Context, arg1 []*share.Blob, arg2 *state.TxConfig) (*types.TxResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SubmitPayForBlob", arg0, arg1, arg2) ret0, _ := ret[0].(*types.TxResponse) diff --git a/nodebuilder/state/state.go b/nodebuilder/state/state.go index 5b63b69450..89365aec90 100644 --- a/nodebuilder/state/state.go +++ b/nodebuilder/state/state.go @@ -5,6 +5,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/state" ) @@ -37,7 +39,7 @@ type Module interface { // SubmitPayForBlob builds, signs and submits a PayForBlob transaction. SubmitPayForBlob( ctx context.Context, - blobs []*state.Blob, + blobs []*libshare.Blob, config *state.TxConfig, ) (*state.TxResponse, error) // CancelUnbondingDelegation cancels a user's pending undelegation from a validator. @@ -97,7 +99,6 @@ type Module interface { } // API is a wrapper around Module for the RPC. -// TODO(@distractedm1nd): These structs need to be autogenerated. // //nolint:dupl type API struct { @@ -113,7 +114,7 @@ type API struct { ) (*state.TxResponse, error) `perm:"write"` SubmitPayForBlob func( ctx context.Context, - blobs []*state.Blob, + blobs []*libshare.Blob, config *state.TxConfig, ) (*state.TxResponse, error) `perm:"write"` CancelUnbondingDelegation func( @@ -187,7 +188,7 @@ func (api *API) Transfer( func (api *API) SubmitPayForBlob( ctx context.Context, - blobs []*state.Blob, + blobs []*libshare.Blob, config *state.TxConfig, ) (*state.TxResponse, error) { return api.Internal.SubmitPayForBlob(ctx, blobs, config) diff --git a/nodebuilder/state/stub.go b/nodebuilder/state/stub.go index c08919fd28..530d42441c 100644 --- a/nodebuilder/state/stub.go +++ b/nodebuilder/state/stub.go @@ -6,6 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/state" ) @@ -42,7 +44,7 @@ func (s stubbedStateModule) Transfer( func (s stubbedStateModule) SubmitPayForBlob( context.Context, - []*state.Blob, + []*libshare.Blob, *state.TxConfig, ) (*state.TxResponse, error) { return nil, ErrNoStateAccess diff --git a/nodebuilder/store.go b/nodebuilder/store.go index 9d8af29075..ead6645c5f 100644 --- a/nodebuilder/store.go +++ b/nodebuilder/store.go @@ -17,10 +17,11 @@ import ( dsbadger "github.com/ipfs/go-ds-badger4" "github.com/mitchellh/go-homedir" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/libs/keystore" nodemod "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" - "github.com/celestiaorg/celestia-node/share" ) var ( @@ -168,7 +169,7 @@ func DiscoverOpened() (string, error) { for _, n := range defaultNetwork { for _, tp := range nodeTypes { - path, err := DefaultNodeStorePath(tp.String(), n.String()) + path, err := DefaultNodeStorePath(tp, n) if err != nil { return "", err } @@ -185,25 +186,26 @@ func DiscoverOpened() (string, error) { // DefaultNodeStorePath constructs the default node store path using the given // node type and network. -var DefaultNodeStorePath = func(tp, network string) (string, error) { +var DefaultNodeStorePath = func(tp nodemod.Type, network p2p.Network) (string, error) { home := os.Getenv("CELESTIA_HOME") + if home != "" { + return home, nil + } - if home == "" { - var err error - home, err = os.UserHomeDir() - if err != nil { - return "", err - } + home, err := os.UserHomeDir() + if err != nil { + return "", err } - if network == p2p.Mainnet.String() { - return fmt.Sprintf("%s/.celestia-%s", home, strings.ToLower(tp)), nil + + if network == p2p.Mainnet { + return fmt.Sprintf("%s/.celestia-%s", home, strings.ToLower(tp.String())), nil } // only include network name in path for testnets and custom networks return fmt.Sprintf( "%s/.celestia-%s-%s", home, - strings.ToLower(tp), - strings.ToLower(network), + strings.ToLower(tp.String()), + strings.ToLower(network.String()), ), nil } @@ -274,10 +276,10 @@ func dataPath(base string) string { func constraintBadgerConfig() *dsbadger.Options { opts := dsbadger.DefaultOptions // this must be copied // ValueLog: - // 2mib default => share.Size - makes sure headers and samples are stored in value log + // 2mib default => libshare.ShareSize - makes sure headers and samples are stored in value log // This *tremendously* reduces the amount of memory used by the node, up to 10 times less during // compaction - opts.ValueThreshold = share.Size + opts.ValueThreshold = libshare.ShareSize // make sure we don't have any limits for stored headers opts.ValueLogMaxEntries = 100000000 // run value log GC more often to spread the work over time diff --git a/nodebuilder/store_test.go b/nodebuilder/store_test.go index a7968a6d72..2b7cdd23de 100644 --- a/nodebuilder/store_test.go +++ b/nodebuilder/store_test.go @@ -3,28 +3,16 @@ package nodebuilder import ( - "context" "fmt" "strconv" "testing" - "time" "github.com/gofrs/flock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" - "github.com/celestiaorg/nmt" - "github.com/celestiaorg/rsmt2d" - "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/sharetest" ) func TestRepo(t *testing.T) { @@ -68,106 +56,11 @@ func TestRepo(t *testing.T) { } } -func BenchmarkStore(b *testing.B) { - ctx, cancel := context.WithCancel(context.Background()) - b.Cleanup(cancel) - - // BenchmarkStore/bench_read_128-10 14 78970661 ns/op (~70ms) - b.Run("bench put 128", func(b *testing.B) { - dir := b.TempDir() - err := Init(*DefaultConfig(node.Full), dir, node.Full) - require.NoError(b, err) - - store := newStore(ctx, b, eds.DefaultParameters(), dir) - size := 128 - b.Run("enabled eds proof caching", func(b *testing.B) { - b.StopTimer() - b.ResetTimer() - for i := 0; i < b.N; i++ { - adder := ipld.NewProofsAdder(size * 2) - shares := sharetest.RandShares(b, size*size) - eds, err := rsmt2d.ComputeExtendedDataSquare( - shares, - share.DefaultRSMT2DCodec(), - wrapper.NewConstructor(uint64(size), - nmt.NodeVisitor(adder.VisitFn())), - ) - require.NoError(b, err) - dah, err := da.NewDataAvailabilityHeader(eds) - require.NoError(b, err) - ctx := ipld.CtxWithProofsAdder(ctx, adder) - - b.StartTimer() - err = store.edsStore.Put(ctx, dah.Hash(), eds) - b.StopTimer() - require.NoError(b, err) - } - }) - - b.Run("disabled eds proof caching", func(b *testing.B) { - b.ResetTimer() - b.StopTimer() - for i := 0; i < b.N; i++ { - eds := edstest.RandEDS(b, size) - dah, err := da.NewDataAvailabilityHeader(eds) - require.NoError(b, err) - - b.StartTimer() - err = store.edsStore.Put(ctx, dah.Hash(), eds) - b.StopTimer() - require.NoError(b, err) - } - }) - }) -} - -func TestStoreRestart(t *testing.T) { - const ( - blocks = 5 - size = 32 - ) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - t.Cleanup(cancel) - - dir := t.TempDir() - err := Init(*DefaultConfig(node.Full), dir, node.Full) - require.NoError(t, err) - - store := newStore(ctx, t, eds.DefaultParameters(), dir) - - hashes := make([][]byte, blocks) - for i := range hashes { - edss := edstest.RandEDS(t, size) - require.NoError(t, err) - dah, err := da.NewDataAvailabilityHeader(edss) - require.NoError(t, err) - err = store.edsStore.Put(ctx, dah.Hash(), edss) - require.NoError(t, err) - - // store hashes for read loop later - hashes[i] = dah.Hash() - } - - // restart store - store.stop(ctx, t) - store = newStore(ctx, t, eds.DefaultParameters(), dir) - - for _, h := range hashes { - edsReader, err := store.edsStore.GetCAR(ctx, h) - require.NoError(t, err) - odsReader, err := eds.ODSReader(edsReader) - require.NoError(t, err) - _, err = eds.ReadEDS(ctx, odsReader, h) - require.NoError(t, err) - require.NoError(t, edsReader.Close()) - } -} - func TestDiscoverOpened(t *testing.T) { t.Run("single open store", func(t *testing.T) { _, dir := initAndOpenStore(t, node.Full) - mockDefaultNodeStorePath := func(t, n string) (string, error) { + mockDefaultNodeStorePath := func(t node.Type, n p2p.Network) (string, error) { return dir, nil } DefaultNodeStorePath = mockDefaultNodeStorePath @@ -193,8 +86,8 @@ func TestDiscoverOpened(t *testing.T) { } } - mockDefaultNodeStorePath := func(tp, n string) (string, error) { - key := n + "_" + tp + mockDefaultNodeStorePath := func(tp node.Type, n p2p.Network) (string, error) { + key := n.String() + "_" + tp.String() if dir, ok := dirMap[key]; ok { return dir, nil } @@ -218,7 +111,7 @@ func TestDiscoverOpened(t *testing.T) { t.Run("no opened store", func(t *testing.T) { dir := t.TempDir() - mockDefaultNodeStorePath := func(t, n string) (string, error) { + mockDefaultNodeStorePath := func(t node.Type, n p2p.Network) (string, error) { return dir, nil } DefaultNodeStorePath = mockDefaultNodeStorePath @@ -262,28 +155,3 @@ func initAndOpenStore(t *testing.T, tp node.Type) (store Store, dir string) { require.NoError(t, err) return store, dir } - -type store struct { - s Store - edsStore *eds.Store -} - -func newStore(ctx context.Context, t testing.TB, params *eds.Parameters, dir string) store { - s, err := OpenStore(dir, nil) - require.NoError(t, err) - ds, err := s.Datastore() - require.NoError(t, err) - edsStore, err := eds.NewStore(params, dir, ds) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - return store{ - s: s, - edsStore: edsStore, - } -} - -func (s *store) stop(ctx context.Context, t *testing.T) { - require.NoError(t, s.edsStore.Stop(ctx)) - require.NoError(t, s.s.Close()) -} diff --git a/nodebuilder/tests/api_test.go b/nodebuilder/tests/api_test.go index 0a4839345e..a43481ca2b 100644 --- a/nodebuilder/tests/api_test.go +++ b/nodebuilder/tests/api_test.go @@ -3,7 +3,11 @@ package tests import ( + "bytes" "context" + "fmt" + "net/http" + "os" "testing" "time" @@ -12,9 +16,11 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/api/rpc/client" + "github.com/celestiaorg/celestia-node/api/rpc/perms" "github.com/celestiaorg/celestia-node/blob" - "github.com/celestiaorg/celestia-node/blob/blobtest" "github.com/celestiaorg/celestia-node/nodebuilder" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp" @@ -107,19 +113,29 @@ func TestBlobRPC(t *testing.T) { rpcClient := getAdminClient(ctx, bridge, t) - appBlobs, err := blobtest.GenerateV0Blobs([]int{8}, false) + libBlobs, err := libshare.GenerateV0Blobs([]int{8}, false) require.NoError(t, err) newBlob, err := blob.NewBlob( - uint8(appBlobs[0].GetShareVersion()), - appBlobs[0].Namespace().Bytes(), - appBlobs[0].GetData(), + libBlobs[0].ShareVersion(), + libBlobs[0].Namespace(), + libBlobs[0].Data(), + libBlobs[0].Signer(), ) require.NoError(t, err) height, err := rpcClient.Blob.Submit(ctx, []*blob.Blob{newBlob}, state.NewTxConfig()) require.NoError(t, err) require.True(t, height != 0) + + txResp, err := rpcClient.State.SubmitPayForBlob(ctx, libBlobs, state.NewTxConfig()) + require.NoError(t, err) + require.NotNil(t, txResp) + require.Equal(t, uint32(0), txResp.Code) + + b, err := rpcClient.Blob.Get(ctx, uint64(txResp.Height), newBlob.Namespace(), newBlob.Commitment) + require.NoError(t, err) + require.NotNil(t, b) } // TestHeaderSubscription ensures that the header subscription over RPC works @@ -166,3 +182,35 @@ func TestHeaderSubscription(t *testing.T) { err = light.Stop(ctx) require.NoError(t, err) } + +func TestSubmitBlobOverHTTP(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout) + t.Cleanup(cancel) + + sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second)) + // start a bridge node + bridge := sw.NewBridgeNode() + err := bridge.Start(ctx) + require.NoError(t, err) + + adminPerms := []auth.Permission{"public", "read", "write", "admin"} + jwt, err := bridge.AdminServ.AuthNew(ctx, adminPerms) + require.NoError(t, err) + + payload, err := os.ReadFile("testdata/submitPFB.json") + require.NoError(t, err) + + bridgeAddr := "http://" + bridge.RPCServer.ListenAddr() + req, err := http.NewRequest("POST", bridgeAddr, bytes.NewBuffer(payload)) + require.NoError(t, err) + + req.Header = http.Header{ + perms.AuthKey: []string{fmt.Sprintf("Bearer %s", jwt)}, + } + + httpClient := &http.Client{Timeout: time.Second * 5} + resp, err := httpClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode) +} diff --git a/nodebuilder/tests/blob_test.go b/nodebuilder/tests/blob_test.go index bd3c94847a..817dd81014 100644 --- a/nodebuilder/tests/blob_test.go +++ b/nodebuilder/tests/blob_test.go @@ -13,13 +13,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - squareblob "github.com/celestiaorg/go-square/blob" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/celestia-node/blob" - "github.com/celestiaorg/celestia-node/blob/blobtest" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp" - "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/state" ) @@ -28,14 +26,14 @@ func TestBlobModule(t *testing.T) { t.Cleanup(cancel) sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second*1)) - appBlobs0, err := blobtest.GenerateV0Blobs([]int{8, 4}, true) + libBlobs0, err := libshare.GenerateV0Blobs([]int{8, 4}, true) require.NoError(t, err) - appBlobs1, err := blobtest.GenerateV0Blobs([]int{4}, false) + libBlobs1, err := libshare.GenerateV0Blobs([]int{4}, false) require.NoError(t, err) - blobs := make([]*blob.Blob, 0, len(appBlobs0)+len(appBlobs1)) + blobs := make([]*blob.Blob, 0, len(libBlobs0)+len(libBlobs1)) - for _, squareBlob := range append(appBlobs0, appBlobs1...) { - blob, err := convert(squareBlob) + for _, libBlob := range append(libBlobs0, libBlobs1...) { + blob, err := convert(libBlob) require.NoError(t, err) blobs = append(blobs, blob) } @@ -61,6 +59,19 @@ func TestBlobModule(t *testing.T) { fullClient := getAdminClient(ctx, fullNode, t) lightClient := getAdminClient(ctx, lightNode, t) + address, err := fullClient.State.AccountAddress(ctx) + require.NoError(t, err) + v1Blob, err := libshare.NewV1Blob( + libshare.MustNewV0Namespace(bytes.Repeat([]byte{5}, libshare.NamespaceVersionZeroIDSize)), + []byte("test data"), + address.Bytes(), + ) + require.NoError(t, err) + + v1, err := convert(v1Blob) + require.NoError(t, err) + blobs = append(blobs, v1) + height, err := fullClient.Blob.Submit(ctx, blobs, state.NewTxConfig()) require.NoError(t, err) @@ -74,32 +85,42 @@ func TestBlobModule(t *testing.T) { doFn func(t *testing.T) }{ { - name: "Get", + name: "GetV0", doFn: func(t *testing.T) { - // https://github.com/celestiaorg/celestia-node/issues/2915 - time.Sleep(time.Second) blob1, err := fullClient.Blob.Get(ctx, height, blobs[0].Namespace(), blobs[0].Commitment) require.NoError(t, err) - require.Equal(t, blobs[0].Commitment, blob1.Commitment) + assert.Equal(t, blobs[0].Commitment, blob1.Commitment) + assert.Equal(t, blobs[0].Data(), blob1.Data()) + assert.Nil(t, blob1.Signer()) }, }, { - name: "GetAll", + name: "GetAllV0", doFn: func(t *testing.T) { - // https://github.com/celestiaorg/celestia-node/issues/2915 - time.Sleep(time.Second) - newBlobs, err := fullClient.Blob.GetAll(ctx, height, []share.Namespace{blobs[0].Namespace()}) + newBlobs, err := fullClient.Blob.GetAll(ctx, height, []libshare.Namespace{blobs[0].Namespace()}) require.NoError(t, err) - require.Len(t, newBlobs, len(appBlobs0)) - require.True(t, bytes.Equal(blobs[0].Commitment, newBlobs[0].Commitment)) - require.True(t, bytes.Equal(blobs[1].Commitment, newBlobs[1].Commitment)) + assert.Len(t, newBlobs, len(libBlobs0)) + assert.Equal(t, blobs[0].Commitment, newBlobs[0].Commitment) + assert.Equal(t, blobs[1].Commitment, newBlobs[1].Commitment) + assert.Nil(t, newBlobs[0].Signer()) + assert.Nil(t, newBlobs[1].Signer()) + }, + }, + { + name: "Get BlobV1", + doFn: func(t *testing.T) { + blobV1, err := fullClient.Blob.Get(ctx, height, v1.Namespace(), v1.Commitment) + require.NoError(t, err) + assert.Equal(t, libshare.ShareVersionOne, blobV1.ShareVersion()) + assert.Equal(t, v1.Commitment, blobV1.Commitment) + assert.NotNil(t, blobV1.Signer()) + assert.Equal(t, blobV1.Signer(), v1.Signer()) + }, }, { name: "Included", doFn: func(t *testing.T) { - // https://github.com/celestiaorg/celestia-node/issues/2915 - time.Sleep(time.Second) proof, err := fullClient.Blob.GetProof(ctx, height, blobs[0].Namespace(), blobs[0].Commitment) require.NoError(t, err) @@ -117,9 +138,9 @@ func TestBlobModule(t *testing.T) { { name: "Not Found", doFn: func(t *testing.T) { - appBlob, err := blobtest.GenerateV0Blobs([]int{4}, false) + libBlob, err := libshare.GenerateV0Blobs([]int{4}, false) require.NoError(t, err) - newBlob, err := convert(appBlob[0]) + newBlob, err := convert(libBlob[0]) require.NoError(t, err) b, err := fullClient.Blob.Get(ctx, height, newBlob.Namespace(), newBlob.Commitment) @@ -127,7 +148,7 @@ func TestBlobModule(t *testing.T) { require.Error(t, err) require.ErrorContains(t, err, blob.ErrBlobNotFound.Error()) - blobs, err := fullClient.Blob.GetAll(ctx, height, []share.Namespace{newBlob.Namespace()}) + blobs, err := fullClient.Blob.GetAll(ctx, height, []libshare.Namespace{newBlob.Namespace()}) require.NoError(t, err) assert.Empty(t, blobs) }, @@ -135,9 +156,9 @@ func TestBlobModule(t *testing.T) { { name: "Submit equal blobs", doFn: func(t *testing.T) { - appBlob, err := blobtest.GenerateV0Blobs([]int{8, 4}, true) + libBlob, err := libshare.GenerateV0Blobs([]int{8, 4}, true) require.NoError(t, err) - b, err := convert(appBlob[0]) + b, err := convert(libBlob[0]) require.NoError(t, err) height, err := fullClient.Blob.Submit(ctx, []*blob.Blob{b, b}, state.NewTxConfig()) @@ -150,11 +171,6 @@ func TestBlobModule(t *testing.T) { require.NoError(t, err) require.Equal(t, b.Commitment, b0.Commitment) - // give some time to store the data, - // otherwise the test will hang on the IPLD level. - // https://github.com/celestiaorg/celestia-node/issues/2915 - time.Sleep(time.Second) - proof, err := fullClient.Blob.GetProof(ctx, height, b.Namespace(), b.Commitment) require.NoError(t, err) @@ -179,11 +195,6 @@ func TestBlobModule(t *testing.T) { require.NoError(t, err) require.Equal(t, blobs[0].Commitment, b0.Commitment) - // give some time to store the data, - // otherwise the test will hang on the IPLD level. - // https://github.com/celestiaorg/celestia-node/issues/2915 - time.Sleep(time.Second) - proof, err := fullClient.Blob.GetProof(ctx, h, blobs[0].Namespace(), blobs[0].Commitment) require.NoError(t, err) @@ -202,8 +213,8 @@ func TestBlobModule(t *testing.T) { } } -// convert converts a squareblob.Blob to a blob.Blob. +// convert converts a libshare.Blob to a blob.Blob. // convert may be deduplicated with convertBlobs from the blob package. -func convert(squareBlob *squareblob.Blob) (nodeBlob *blob.Blob, err error) { - return blob.NewBlob(uint8(squareBlob.GetShareVersion()), squareBlob.Namespace().Bytes(), squareBlob.GetData()) +func convert(libBlob *libshare.Blob) (nodeBlob *blob.Blob, err error) { + return blob.NewBlob(libBlob.ShareVersion(), libBlob.Namespace(), libBlob.Data(), libBlob.Signer()) } diff --git a/nodebuilder/tests/da_test.go b/nodebuilder/tests/da_test.go index 292227881d..c9973f4540 100644 --- a/nodebuilder/tests/da_test.go +++ b/nodebuilder/tests/da_test.go @@ -12,14 +12,13 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/celestia-node/blob" - "github.com/celestiaorg/celestia-node/blob/blobtest" "github.com/celestiaorg/celestia-node/nodebuilder/da" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp" - "github.com/celestiaorg/celestia-node/share" ) func TestDaModule(t *testing.T) { @@ -27,25 +26,27 @@ func TestDaModule(t *testing.T) { t.Cleanup(cancel) sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second)) - namespace, err := share.NewBlobNamespaceV0([]byte("namespace")) + namespace, err := libshare.NewV0Namespace([]byte("namespace")) require.NoError(t, err) + require.False(t, namespace.IsReserved()) - appBlobs0, err := blobtest.GenerateV0Blobs([]int{8, 4}, true) + libBlobs0, err := libshare.GenerateV0Blobs([]int{8, 4}, true) require.NoError(t, err) - appBlobs1, err := blobtest.GenerateV0Blobs([]int{4}, false) + libBlobs1, err := libshare.GenerateV0Blobs([]int{4}, false) require.NoError(t, err) - blobs := make([]*blob.Blob, 0, len(appBlobs0)+len(appBlobs1)) - daBlobs := make([][]byte, 0, len(appBlobs0)+len(appBlobs1)) + blobs := make([]*blob.Blob, 0, len(libBlobs0)+len(libBlobs1)) + daBlobs := make([][]byte, 0, len(libBlobs0)+len(libBlobs1)) - for _, squareBlob := range append(appBlobs0, appBlobs1...) { + for _, libBlob := range append(libBlobs0, libBlobs1...) { blob, err := blob.NewBlob( - uint8(squareBlob.GetShareVersion()), - squareBlob.Namespace().Bytes(), - squareBlob.GetData(), + libBlob.ShareVersion(), + libBlob.Namespace(), + libBlob.Data(), + libBlob.Signer(), ) require.NoError(t, err) blobs = append(blobs, blob) - daBlobs = append(daBlobs, blob.Data) + daBlobs = append(daBlobs, blob.Data()) } require.NoError(t, err) @@ -71,7 +72,7 @@ func TestDaModule(t *testing.T) { fullClient := getAdminClient(ctx, fullNode, t) lightClient := getAdminClient(ctx, lightNode, t) - ids, err := fullClient.DA.Submit(ctx, daBlobs, -1, namespace) + ids, err := fullClient.DA.Submit(ctx, daBlobs, -1, namespace.Bytes()) require.NoError(t, err) test := []struct { @@ -89,13 +90,12 @@ func TestDaModule(t *testing.T) { { name: "GetProofs + Validate", doFn: func(t *testing.T) { - t.Skip() h, _ := da.SplitID(ids[0]) lightClient.Header.WaitForHeight(ctx, h) - proofs, err := lightClient.DA.GetProofs(ctx, ids, namespace) + proofs, err := lightClient.DA.GetProofs(ctx, ids, namespace.Bytes()) require.NoError(t, err) require.NotEmpty(t, proofs) - valid, err := fullClient.DA.Validate(ctx, ids, proofs, namespace) + valid, err := fullClient.DA.Validate(ctx, ids, proofs, namespace.Bytes()) require.NoError(t, err) for _, v := range valid { require.True(t, v) @@ -105,11 +105,13 @@ func TestDaModule(t *testing.T) { { name: "GetIDs", doFn: func(t *testing.T) { - t.Skip() height, _ := da.SplitID(ids[0]) - ids2, err := fullClient.DA.GetIDs(ctx, height, namespace) + result, err := fullClient.DA.GetIDs(ctx, height, namespace.Bytes()) require.NoError(t, err) - require.EqualValues(t, ids, ids2) + require.EqualValues(t, ids, result.IDs) + header, err := lightClient.Header.GetByHeight(ctx, height) + require.NoError(t, err) + require.EqualValues(t, header.Time(), result.Timestamp) }, }, { @@ -117,7 +119,7 @@ func TestDaModule(t *testing.T) { doFn: func(t *testing.T) { h, _ := da.SplitID(ids[0]) lightClient.Header.WaitForHeight(ctx, h) - fetched, err := lightClient.DA.Get(ctx, ids, namespace) + fetched, err := lightClient.DA.Get(ctx, ids, namespace.Bytes()) require.NoError(t, err) require.Len(t, fetched, len(ids)) for i := range fetched { @@ -128,8 +130,7 @@ func TestDaModule(t *testing.T) { { name: "Commit", doFn: func(t *testing.T) { - t.Skip() - fetched, err := fullClient.DA.Commit(ctx, ids, namespace) + fetched, err := fullClient.DA.Commit(ctx, daBlobs, namespace.Bytes()) require.NoError(t, err) require.Len(t, fetched, len(ids)) for i := range fetched { @@ -138,10 +139,33 @@ func TestDaModule(t *testing.T) { } }, }, + { + name: "SubmitWithOptions - valid", + doFn: func(t *testing.T) { + ids, err := fullClient.DA.SubmitWithOptions(ctx, daBlobs, -1, namespace.Bytes(), []byte(`{"key_name": "validator"}`)) + require.NoError(t, err) + require.NotEmpty(t, ids) + }, + }, + { + name: "SubmitWithOptions - invalid JSON", + doFn: func(t *testing.T) { + ids, err := fullClient.DA.SubmitWithOptions(ctx, daBlobs, -1, namespace.Bytes(), []byte("not JSON")) + require.Error(t, err) + require.Nil(t, ids) + }, + }, + { + name: "SubmitWithOptions - invalid key name", + doFn: func(t *testing.T) { + ids, err := fullClient.DA.SubmitWithOptions(ctx, daBlobs, -1, namespace.Bytes(), []byte(`{"key_name": "invalid"}`)) + require.Error(t, err) + require.Nil(t, ids) + }, + }, } for _, tt := range test { - tt := tt t.Run(tt.name, func(t *testing.T) { tt.doFn(t) }) diff --git a/nodebuilder/tests/fraud_test.go b/nodebuilder/tests/fraud_test.go index c5d1cc7e0f..524b306ac0 100644 --- a/nodebuilder/tests/fraud_test.go +++ b/nodebuilder/tests/fraud_test.go @@ -7,8 +7,6 @@ import ( "testing" "time" - "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" @@ -23,8 +21,8 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/core" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp" - "github.com/celestiaorg/celestia-node/share/eds" "github.com/celestiaorg/celestia-node/share/eds/byzantine" + "github.com/celestiaorg/celestia-node/store" ) /* @@ -46,7 +44,7 @@ Another note: this test disables share exchange to speed up test results. 9. Try to start a Full Node(FN) that contains a BEFP in its store. */ func TestFraudProofHandling(t *testing.T) { - t.Skip("skipping to please the CI and shwap skips it anyway") + t.Skip("unsupported temporary") ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout) t.Cleanup(cancel) @@ -62,11 +60,9 @@ func TestFraudProofHandling(t *testing.T) { set, val := sw.Validators(t) fMaker := headerfraud.NewFraudMaker(t, 10, []types.PrivValidator{val}, set) - storeCfg := eds.DefaultParameters() - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - edsStore, err := eds.NewStore(storeCfg, t.TempDir(), ds) + storeCfg := store.DefaultParameters() + edsStore, err := store.NewStore(storeCfg, t.TempDir()) require.NoError(t, err) - require.NoError(t, edsStore.Start(ctx)) t.Cleanup(func() { _ = edsStore.Stop(ctx) }) diff --git a/nodebuilder/tests/helpers_test.go b/nodebuilder/tests/helpers_test.go index 978b66553d..4e5db228b6 100644 --- a/nodebuilder/tests/helpers_test.go +++ b/nodebuilder/tests/helpers_test.go @@ -20,7 +20,7 @@ func getAdminClient(ctx context.Context, nd *nodebuilder.Node, t *testing.T) *cl signer := nd.AdminSigner listenAddr := "ws://" + nd.RPCServer.ListenAddr() - jwt, err := authtoken.NewSignedJWT(signer, []auth.Permission{"public", "read", "write", "admin"}) + jwt, err := authtoken.NewSignedJWT(signer, []auth.Permission{"public", "read", "write", "admin"}, time.Minute) require.NoError(t, err) client, err := client.NewClient(ctx, listenAddr, jwt) @@ -30,6 +30,5 @@ func getAdminClient(ctx context.Context, nd *nodebuilder.Node, t *testing.T) *cl } func setTimeInterval(cfg *nodebuilder.Config, interval time.Duration) { - cfg.P2P.RoutingTableRefreshPeriod = interval cfg.Share.Discovery.AdvertiseInterval = interval } diff --git a/nodebuilder/tests/nd_test.go b/nodebuilder/tests/nd_test.go index d82f405a0c..668abb71cf 100644 --- a/nodebuilder/tests/nd_test.go +++ b/nodebuilder/tests/nd_test.go @@ -12,14 +12,17 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/fx" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/nodebuilder" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" "github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/getters" - "github.com/celestiaorg/celestia-node/share/p2p/shrexnd" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/getters" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrex_getter" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexnd" + "github.com/celestiaorg/celestia-node/store" ) func TestShrexNDFromLights(t *testing.T) { @@ -65,11 +68,14 @@ func TestShrexNDFromLights(t *testing.T) { reqCtx, cancel := context.WithTimeout(ctx, time.Second*5) // ensure to fetch random namespace (not the reserved namespace) - namespace := h.DAH.RowRoots[1][:share.NamespaceSize] + namespace := h.DAH.RowRoots[1][:libshare.NamespaceSize] + ns, err := libshare.NewNamespaceFromBytes(namespace) + require.NoError(t, err) - expected, err := bridgeClient.Share.GetSharesByNamespace(reqCtx, h, namespace) + height := h.Height() + expected, err := bridgeClient.Share.GetNamespaceData(reqCtx, height, ns) require.NoError(t, err) - got, err := lightClient.Share.GetSharesByNamespace(reqCtx, h, namespace) + got, err := lightClient.Share.GetNamespaceData(reqCtx, height, ns) require.NoError(t, err) require.True(t, len(got[0].Shares) > 0) @@ -139,20 +145,22 @@ func TestShrexNDFromLightsWithBadFulls(t *testing.T) { reqCtx, cancel := context.WithTimeout(ctx, time.Second*5) // ensure to fetch random namespace (not the reserved namespace) - namespace := h.DAH.RowRoots[1][:share.NamespaceSize] - - expected, err := bridgeClient.Share.GetSharesByNamespace(reqCtx, h, namespace) + namespace := h.DAH.RowRoots[1][:libshare.NamespaceSize] + height := h.Height() + ns, err := libshare.NewNamespaceFromBytes(namespace) + require.NoError(t, err) + expected, err := bridgeClient.Share.GetNamespaceData(reqCtx, height, ns) require.NoError(t, err) require.True(t, len(expected[0].Shares) > 0) // choose a random full to test fN := fulls[len(fulls)/2] fnClient := getAdminClient(ctx, fN, t) - gotFull, err := fnClient.Share.GetSharesByNamespace(reqCtx, h, namespace) + gotFull, err := fnClient.Share.GetNamespaceData(reqCtx, height, ns) require.NoError(t, err) require.True(t, len(gotFull[0].Shares) > 0) - gotLight, err := lightClient.Share.GetSharesByNamespace(reqCtx, h, namespace) + gotLight, err := lightClient.Share.GetNamespaceData(reqCtx, height, ns) require.NoError(t, err) require.True(t, len(gotLight[0].Shares) > 0) @@ -177,7 +185,7 @@ func replaceNDServer(cfg *nodebuilder.Config, handler network.StreamHandler) fx. return fx.Decorate(fx.Annotate( func( host host.Host, - store *eds.Store, + store *store.Store, network p2p.Network, ) (*shrexnd.Server, error) { cfg.Share.ShrExNDParams.WithNetworkID(network.String()) @@ -198,12 +206,12 @@ func replaceShareGetter() fx.Option { return fx.Decorate(fx.Annotate( func( host host.Host, - store *eds.Store, - storeGetter *getters.StoreGetter, - shrexGetter *getters.ShrexGetter, + store *store.Store, + storeGetter *store.Getter, + shrexGetter *shrex_getter.Getter, network p2p.Network, - ) share.Getter { - cascade := make([]share.Getter, 0, 2) + ) shwap.Getter { + cascade := make([]shwap.Getter, 0, 2) cascade = append(cascade, storeGetter) cascade = append(cascade, shrexGetter) return getters.NewCascadeGetter(cascade) diff --git a/nodebuilder/tests/prune_test.go b/nodebuilder/tests/prune_test.go index 59b665e141..e9965a4fa9 100644 --- a/nodebuilder/tests/prune_test.go +++ b/nodebuilder/tests/prune_test.go @@ -1,28 +1,35 @@ +//go:build pruning || integration + package tests import ( "bytes" "context" + "encoding/json" "testing" "time" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" "github.com/libp2p/go-libp2p/core/host" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/fx" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/blob" "github.com/celestiaorg/celestia-node/libs/fxutil" "github.com/celestiaorg/celestia-node/nodebuilder" "github.com/celestiaorg/celestia-node/nodebuilder/das" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp" - "github.com/celestiaorg/celestia-node/pruner" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/getters" - "github.com/celestiaorg/celestia-node/share/p2p/peers" - "github.com/celestiaorg/celestia-node/share/p2p/shrexeds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexnd" + full_avail "github.com/celestiaorg/celestia-node/share/availability/full" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/peers" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrex_getter" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexeds" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexnd" ) // TestArchivalBlobSync tests whether a LN is able to sync historical blobs from @@ -64,22 +71,22 @@ func TestArchivalBlobSync(t *testing.T) { pruningCfg := nodebuilder.DefaultConfig(node.Bridge) pruningCfg.Pruner.EnableService = true - testAvailWindow := pruner.AvailabilityWindow(time.Millisecond) + testAvailWindow := time.Millisecond prunerOpts := fx.Options( fx.Replace(testAvailWindow), fxutil.ReplaceAs(func( edsClient *shrexeds.Client, ndClient *shrexnd.Client, managers map[string]*peers.Manager, - ) *getters.ShrexGetter { - return getters.NewShrexGetter( + ) *shrex_getter.Getter { + return shrex_getter.NewGetter( edsClient, ndClient, managers["full"], managers["archival"], testAvailWindow, ) - }, new(getters.ShrexGetter)), + }, new(shrex_getter.Getter)), ) // stop the archival BN to force LN to have to discover @@ -118,22 +125,20 @@ func TestArchivalBlobSync(t *testing.T) { eh, err := archivalFN.HeaderServ.GetByHeight(ctx, uint64(i)) require.NoError(t, err) - if bytes.Equal(eh.DataHash, share.EmptyRoot().Hash()) { + if bytes.Equal(eh.DataHash, share.EmptyEDSRoots().Hash()) { i++ continue } - shr, err := archivalFN.ShareServ.GetShare(ctx, eh, 2, 2) - require.NoError(t, err) - ns, err := share.NamespaceFromBytes(shr[:share.NamespaceSize]) + shr, err := archivalFN.ShareServ.GetShare(ctx, eh.Height(), 2, 2) require.NoError(t, err) - blobs, err := archivalFN.BlobServ.GetAll(ctx, uint64(i), []share.Namespace{ns}) + blobs, err := archivalFN.BlobServ.GetAll(ctx, eh.Height(), []libshare.Namespace{shr.Namespace()}) require.NoError(t, err) archivalBlobs = append(archivalBlobs, &archivalBlob{ blob: blobs[0], - height: uint64(i), + height: eh.Height(), root: eh.DAH.Hash(), }) @@ -147,7 +152,7 @@ func TestArchivalBlobSync(t *testing.T) { // with the historical blobs for _, pruned := range pruningFulls { for _, b := range archivalBlobs { - has, err := pruned.EDSStore.Has(ctx, b.root) + has, err := pruned.EDSStore.HasByHeight(ctx, b.height) require.NoError(t, err) assert.False(t, has) } @@ -165,16 +170,16 @@ func TestArchivalBlobSync(t *testing.T) { got, err := ln.BlobServ.Get(ctx, b.height, b.blob.Namespace(), b.blob.Commitment) require.NoError(t, err) assert.Equal(t, b.blob.Commitment, got.Commitment) - assert.Equal(t, b.blob.Data, got.Data) + assert.Equal(t, b.blob.Data(), got.Data()) } } -func TestConvertFromPrunedToArchival(t *testing.T) { +func TestDisallowConvertFromPrunedToArchival(t *testing.T) { sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second)) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) t.Cleanup(cancel) - // Light nodes are allowed to disable pruning in wish + // Light nodes have pruning enabled by default for _, nt := range []node.Type{node.Bridge, node.Full} { pruningCfg := nodebuilder.DefaultConfig(nt) pruningCfg.Pruner.EnableService = true @@ -189,6 +194,93 @@ func TestConvertFromPrunedToArchival(t *testing.T) { err = store.PutConfig(archivalCfg) require.NoError(t, err) _, err = sw.NewNodeWithStore(nt, store) - require.ErrorIs(t, err, pruner.ErrDisallowRevertToArchival, nt.String()) + assert.Error(t, err) + assert.ErrorIs(t, full_avail.ErrDisallowRevertToArchival, err) + } +} + +func TestDisallowConvertToArchivalViaLastPrunedCheck(t *testing.T) { + sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second)) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + t.Cleanup(cancel) + + var cp struct { + LastPrunedHeight uint64 `json:"last_pruned_height"` + FailedHeaders map[uint64]struct{} `json:"failed"` + } + + for _, nt := range []node.Type{node.Bridge, node.Full} { + archivalCfg := nodebuilder.DefaultConfig(nt) + + store := nodebuilder.MockStore(t, archivalCfg) + ds, err := store.Datastore() + require.NoError(t, err) + + cp.LastPrunedHeight = 500 + cp.FailedHeaders = make(map[uint64]struct{}) + bin, err := json.Marshal(cp) + require.NoError(t, err) + + prunerStore := namespace.Wrap(ds, datastore.NewKey("pruner")) + err = prunerStore.Put(ctx, datastore.NewKey("checkpoint"), bin) + require.NoError(t, err) + + _, err = sw.NewNodeWithStore(nt, store) + require.Error(t, err) + assert.ErrorIs(t, full_avail.ErrDisallowRevertToArchival, err) + } +} + +func TestConvertFromArchivalToPruned(t *testing.T) { + sw := swamp.NewSwamp(t, swamp.WithBlockTime(time.Second)) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + t.Cleanup(cancel) + + var cp struct { + LastPrunedHeight uint64 `json:"last_pruned_height"` + FailedHeaders map[uint64]struct{} `json:"failed"` + } + + for _, nt := range []node.Type{node.Bridge, node.Full} { + archivalCfg := nodebuilder.DefaultConfig(nt) + + store := nodebuilder.MockStore(t, archivalCfg) + ds, err := store.Datastore() + require.NoError(t, err) + + // the archival node has trimmed up to height 500 + fullAvailStore := namespace.Wrap(ds, datastore.NewKey("full_avail")) + err = fullAvailStore.Put(ctx, datastore.NewKey("previous_mode"), []byte("archival")) + require.NoError(t, err) + + cp.LastPrunedHeight = 500 + cp.FailedHeaders = make(map[uint64]struct{}) + bin, err := json.Marshal(cp) + require.NoError(t, err) + + prunerStore := namespace.Wrap(ds, datastore.NewKey("pruner")) + err = prunerStore.Put(ctx, datastore.NewKey("checkpoint"), bin) + require.NoError(t, err) + + archivalNode := sw.MustNewNodeWithStore(nt, store) + err = archivalNode.Start(ctx) + require.NoError(t, err) + err = archivalNode.Stop(ctx) + require.NoError(t, err) + + // convert to pruned node + pruningCfg := nodebuilder.DefaultConfig(nt) + pruningCfg.Pruner.EnableService = true + err = store.PutConfig(pruningCfg) + require.NoError(t, err) + _, err = sw.NewNodeWithStore(nt, store) + assert.NoError(t, err) + + // expect that the checkpoint has been overridden + bin, err = prunerStore.Get(ctx, datastore.NewKey("checkpoint")) + require.NoError(t, err) + err = json.Unmarshal(bin, &cp) + require.NoError(t, err) + assert.Equal(t, uint64(1), cp.LastPrunedHeight) } } diff --git a/nodebuilder/tests/reconstruct_test.go b/nodebuilder/tests/reconstruct_test.go index 98789cbcc1..36cdf3a6da 100644 --- a/nodebuilder/tests/reconstruct_test.go +++ b/nodebuilder/tests/reconstruct_test.go @@ -36,6 +36,7 @@ Steps: 5. Check that a FN can retrieve shares from 1 to 20 blocks */ func TestFullReconstructFromBridge(t *testing.T) { + t.Skip() const ( blocks = 20 bsize = 16 @@ -74,7 +75,7 @@ func TestFullReconstructFromBridge(t *testing.T) { return err } - return fullClient.Share.SharesAvailable(bctx, h) + return fullClient.Share.SharesAvailable(bctx, h.Height()) }) } require.NoError(t, <-fillDn) @@ -86,6 +87,7 @@ Test-Case: Full Node reconstructs blocks from each other, after unsuccessfully s block from LN subnetworks. Analog to TestShareAvailable_DisconnectedFullNodes. */ func TestFullReconstructFromFulls(t *testing.T) { + t.Skip() if testing.Short() { t.Skip() } @@ -212,10 +214,10 @@ func TestFullReconstructFromFulls(t *testing.T) { ctxErr, cancelErr := context.WithTimeout(ctx, time.Second*30) errg, errCtx = errgroup.WithContext(ctxErr) errg.Go(func() error { - return fullClient1.Share.SharesAvailable(errCtx, h) + return fullClient1.Share.SharesAvailable(errCtx, h.Height()) }) errg.Go(func() error { - return fullClient1.Share.SharesAvailable(errCtx, h) + return fullClient1.Share.SharesAvailable(errCtx, h.Height()) }) require.Error(t, errg.Wait()) cancelErr() @@ -228,10 +230,10 @@ func TestFullReconstructFromFulls(t *testing.T) { h, err := fullClient1.Header.WaitForHeight(bctx, uint64(i)) require.NoError(t, err) errg.Go(func() error { - return fullClient1.Share.SharesAvailable(bctx, h) + return fullClient1.Share.SharesAvailable(bctx, h.Height()) }) errg.Go(func() error { - return fullClient2.Share.SharesAvailable(bctx, h) + return fullClient2.Share.SharesAvailable(bctx, h.Height()) }) } @@ -256,6 +258,7 @@ Steps: 9. Check that the FN can retrieve shares from 1 to 20 blocks */ func TestFullReconstructFromLights(t *testing.T) { + t.Skip() if testing.Short() { t.Skip() } @@ -349,7 +352,7 @@ func TestFullReconstructFromLights(t *testing.T) { return err } - return fullClient.Share.SharesAvailable(bctx, h) + return fullClient.Share.SharesAvailable(bctx, h.Height()) }) } require.NoError(t, <-fillDn) diff --git a/nodebuilder/tests/swamp/config.go b/nodebuilder/tests/swamp/config.go index 39740c6d18..7e00541b61 100644 --- a/nodebuilder/tests/swamp/config.go +++ b/nodebuilder/tests/swamp/config.go @@ -3,7 +3,7 @@ package swamp import ( "time" - "github.com/celestiaorg/celestia-app/v2/test/util/testnode" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" "github.com/celestiaorg/celestia-node/core" ) diff --git a/nodebuilder/tests/swamp/swamp.go b/nodebuilder/tests/swamp/swamp.go index e601ba5a13..57d46bb64b 100644 --- a/nodebuilder/tests/swamp/swamp.go +++ b/nodebuilder/tests/swamp/swamp.go @@ -9,8 +9,6 @@ import ( "testing" "time" - ds "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" @@ -21,7 +19,7 @@ import ( "go.uber.org/fx" "golang.org/x/exp/maps" - "github.com/celestiaorg/celestia-app/v2/test/util/testnode" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/core" @@ -33,7 +31,7 @@ import ( "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" "github.com/celestiaorg/celestia-node/nodebuilder/state" - "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/store" ) var blackholeIP6 = net.ParseIP("100::") @@ -178,8 +176,7 @@ func (s *Swamp) setupGenesis() { // ensure core has surpassed genesis block s.WaitTillHeight(ctx, 2) - ds := ds_sync.MutexWrap(ds.NewMapDatastore()) - store, err := eds.NewStore(eds.DefaultParameters(), s.t.TempDir(), ds) + store, err := store.NewStore(store.DefaultParameters(), s.t.TempDir()) require.NoError(s.t, err) ex, err := core.NewExchange( diff --git a/nodebuilder/tests/swamp/swamp_tx.go b/nodebuilder/tests/swamp/swamp_tx.go index 3cfe904ab1..4d6ce6497a 100644 --- a/nodebuilder/tests/swamp/swamp_tx.go +++ b/nodebuilder/tests/swamp/swamp_tx.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/celestiaorg/celestia-app/v2/test/util/testnode" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" ) // FillBlocks produces the given amount of contiguous blocks with customizable size. diff --git a/nodebuilder/tests/sync_test.go b/nodebuilder/tests/sync_test.go index 90baabd218..48de6376a3 100644 --- a/nodebuilder/tests/sync_test.go +++ b/nodebuilder/tests/sync_test.go @@ -77,7 +77,7 @@ func TestSyncAgainstBridge_NonEmptyChain(t *testing.T) { assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks)) // check that the light node has also sampled over the block at height 20 - err = lightClient.Share.SharesAvailable(ctx, h) + err = lightClient.Share.SharesAvailable(ctx, h.Height()) assert.NoError(t, err) // wait until the entire chain (up to network head) has been sampled @@ -97,7 +97,7 @@ func TestSyncAgainstBridge_NonEmptyChain(t *testing.T) { assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks)) // check to ensure the full node can sync the 20th block's data - err = fullClient.Share.SharesAvailable(ctx, h) + err = fullClient.Share.SharesAvailable(ctx, h.Height()) assert.NoError(t, err) // wait for full node to sync up the blocks from genesis -> network head. @@ -167,7 +167,7 @@ func TestSyncAgainstBridge_EmptyChain(t *testing.T) { assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks)) // check that the light node has also sampled over the block at height 20 - err = lightClient.Share.SharesAvailable(ctx, h) + err = lightClient.Share.SharesAvailable(ctx, h.Height()) assert.NoError(t, err) // wait until the entire chain (up to network head) has been sampled @@ -187,7 +187,7 @@ func TestSyncAgainstBridge_EmptyChain(t *testing.T) { assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks)) // check to ensure the full node can sync the 20th block's data - err = fullClient.Share.SharesAvailable(ctx, h) + err = fullClient.Share.SharesAvailable(ctx, h.Height()) assert.NoError(t, err) // wait for full node to sync up the blocks from genesis -> network head. @@ -250,6 +250,7 @@ func TestSyncStartStopLightWithBridge(t *testing.T) { light = sw.NewLightNode() require.NoError(t, light.Start(ctx)) + lightClient = getAdminClient(ctx, light, t) // ensure when light node comes back up, it can sync the remainder of the chain it // missed while sleeping diff --git a/nodebuilder/tests/testdata/submitPFB.json b/nodebuilder/tests/testdata/submitPFB.json new file mode 100644 index 0000000000..e4e5c68017 --- /dev/null +++ b/nodebuilder/tests/testdata/submitPFB.json @@ -0,0 +1,14 @@ +{ + "jsonrpc": "2.0", + "id": 1, + "method": "state.SubmitPayForBlob", + "params": [ + [{ + "namespace_id": "AAAAAAAAAAAAAAAAAAAAAAAAOUuI3+tg+3TyEw==", + "data": "4PdKsZcCJsDR/BErNf8Q0al/DMHmV606NylMHKBjzutMuJrcQr11as52hPPv4P2X2zccCUUALfq0N4SITHVBNUa15j4y+t/+WkyIZ8IDe+ZHYVX9z0AQWQVW6AZ70BJHZYuhWQ1xJZp2BIeqh0xVYVhauB7DmgXR66dBBEfSuMewl9NKkNINNhAvIeW/NHLS41y4FWi0v2zH8HyA+IJjTO7okTgii1xz5yQ5wZ6UileWLuNldqH9/+77bf4M6J+n+Dtf6LL0QbwpEG1M6f2iZAJsSYCb680Q9xkrMyLFSdgYL3wPirloC64n1p5MuwYiY5o6skqJ1kGDWI4ASr+9NPIV0GdiW76FhsBuimqO3d+wKhvrPdheH/yPRx0Xq6ky9azT/Iq6KL5VW/978JvT7Mk7sVSU0vFoE8zL9ZyGRtW5yfADxY5tGWjDVKtknOv+STLGnLHeJAAPVjFebWuI7it3UvKhOPPXrdYBr6zF4uVyzNS9UsK87SjtkNLSXn4loIcsupnA9VSmqZxYI+AtFd2AspQ8hzOwQoPiLWVDyS9KJOtHCCRgFJmG5cZ9ZlXHOwQh8iHvBWNvOV25XJs2Cm4wYjEu5vCVrh04xB+U7Pmr+ANWwjth6w6qa0SkQf9ck2zPBElkv50Ajjx5jK+BOAES78E=", + "share_version": 0, + "namespace_version": 0 + }], + {} + ] +} \ No newline at end of file diff --git a/pruner/archival/pruner.go b/pruner/archival/pruner.go deleted file mode 100644 index a1a55db0da..0000000000 --- a/pruner/archival/pruner.go +++ /dev/null @@ -1,20 +0,0 @@ -package archival - -import ( - "context" - - "github.com/celestiaorg/celestia-node/header" -) - -// Pruner is a noop implementation of the pruner.Factory interface -// that allows archival nodes to sync and retain historical data -// that is out of the availability window. -type Pruner struct{} - -func NewPruner() *Pruner { - return &Pruner{} -} - -func (p *Pruner) Prune(context.Context, *header.ExtendedHeader) error { - return nil -} diff --git a/pruner/archival/window.go b/pruner/archival/window.go deleted file mode 100644 index b89a779816..0000000000 --- a/pruner/archival/window.go +++ /dev/null @@ -1,5 +0,0 @@ -package archival - -import "github.com/celestiaorg/celestia-node/pruner" - -const Window = pruner.AvailabilityWindow(0) diff --git a/pruner/checkpoint.go b/pruner/checkpoint.go index 329a5c2add..7e8195ca57 100644 --- a/pruner/checkpoint.go +++ b/pruner/checkpoint.go @@ -7,18 +7,11 @@ import ( "fmt" "github.com/ipfs/go-datastore" - "github.com/ipfs/go-datastore/namespace" "github.com/celestiaorg/celestia-node/header" ) var ( - // ErrDisallowRevertToArchival is returned when a node has been run with pruner enabled before and - // launching it with archival mode. - ErrDisallowRevertToArchival = errors.New( - "node has been run with pruner enabled before, it is not safe to convert to an archival" + - "Run with --experimental-pruning enabled or consider re-initializing the store") - storePrefix = datastore.NewKey("pruner") checkpointKey = datastore.NewKey("checkpoint") errCheckpointNotFound = errors.New("checkpoint not found") @@ -31,17 +24,11 @@ type checkpoint struct { FailedHeaders map[uint64]struct{} `json:"failed"` } -// DetectPreviousRun checks if the pruner has run before by checking for the existence of a -// checkpoint. -func DetectPreviousRun(ctx context.Context, ds datastore.Datastore) error { - _, err := getCheckpoint(ctx, namespace.Wrap(ds, storePrefix)) - if errors.Is(err, errCheckpointNotFound) { - return nil - } - if err != nil { - return fmt.Errorf("failed to load checkpoint: %w", err) +func newCheckpoint() *checkpoint { + return &checkpoint{ + LastPrunedHeight: 1, + FailedHeaders: map[uint64]struct{}{}, } - return ErrDisallowRevertToArchival } // storeCheckpoint persists the checkpoint to disk. @@ -78,10 +65,7 @@ func (s *Service) loadCheckpoint(ctx context.Context) error { cp, err := getCheckpoint(ctx, s.ds) if err != nil { if errors.Is(err, errCheckpointNotFound) { - s.checkpoint = &checkpoint{ - LastPrunedHeight: 1, - FailedHeaders: map[uint64]struct{}{}, - } + s.checkpoint = newCheckpoint() return storeCheckpoint(ctx, s.ds, s.checkpoint) } return err diff --git a/pruner/find.go b/pruner/find.go index fd966d6c15..aba621ccbc 100644 --- a/pruner/find.go +++ b/pruner/find.go @@ -18,7 +18,7 @@ func (s *Service) findPruneableHeaders( ctx context.Context, lastPruned *header.ExtendedHeader, ) ([]*header.ExtendedHeader, error) { - pruneCutoff := time.Now().UTC().Add(-s.window.Duration()) + pruneCutoff := time.Now().UTC().Add(-s.window) if !lastPruned.Time().UTC().Before(pruneCutoff) { // this can happen when the network is young and all blocks @@ -31,7 +31,7 @@ func (s *Service) findPruneableHeaders( return nil, err } - if lastPruned.Height() == estimatedCutoffHeight { + if lastPruned.Height() >= estimatedCutoffHeight { // nothing left to prune return nil, nil } @@ -39,7 +39,9 @@ func (s *Service) findPruneableHeaders( log.Debugw("finder: fetching header range", "last pruned", lastPruned.Height(), "target height", estimatedCutoffHeight) - headers, err := s.getter.GetRangeByHeight(ctx, lastPruned, estimatedCutoffHeight) + // GetRangeByHeight requests (from:to), where `to` is non-inclusive, we need + // to request one more header than the estimated cutoff + headers, err := s.getter.GetRangeByHeight(ctx, lastPruned, estimatedCutoffHeight+1) if err != nil { log.Errorw("failed to get range from header store", "from", lastPruned.Height(), "to", estimatedCutoffHeight, "error", err) diff --git a/pruner/full/pruner.go b/pruner/full/pruner.go deleted file mode 100644 index 49967b5050..0000000000 --- a/pruner/full/pruner.go +++ /dev/null @@ -1,40 +0,0 @@ -package full - -import ( - "context" - "errors" - - "github.com/filecoin-project/dagstore" - logging "github.com/ipfs/go-log/v2" - - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" -) - -var log = logging.Logger("pruner/full") - -type Pruner struct { - store *eds.Store -} - -func NewPruner(store *eds.Store) *Pruner { - return &Pruner{ - store: store, - } -} - -func (p *Pruner) Prune(ctx context.Context, eh *header.ExtendedHeader) error { - // short circuit on empty roots - if eh.DAH.Equals(share.EmptyRoot()) { - return nil - } - - log.Debugf("pruning header %s", eh.DAH.Hash()) - - err := p.store.Remove(ctx, eh.DAH.Hash()) - if err != nil && !errors.Is(err, dagstore.ErrShardUnknown) { - return err - } - return nil -} diff --git a/pruner/full/window.go b/pruner/full/window.go deleted file mode 100644 index 4ad69234e2..0000000000 --- a/pruner/full/window.go +++ /dev/null @@ -1,12 +0,0 @@ -package full - -import ( - "time" - - "github.com/celestiaorg/celestia-node/pruner" - "github.com/celestiaorg/celestia-node/pruner/light" -) - -// Window is the availability window for light nodes in the Celestia -// network (30 days + 1 hour). -const Window = pruner.AvailabilityWindow(time.Duration(light.Window) + time.Hour) diff --git a/pruner/full/window_test.go b/pruner/full/window_test.go deleted file mode 100644 index 9ee6d7c89a..0000000000 --- a/pruner/full/window_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package full - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -// TestFullWindowConst exists to ensure that any changes to the sampling window -// are deliberate. -func TestFullWindowConst(t *testing.T) { - assert.Equal(t, Window.Duration(), (30*24*time.Hour)+time.Hour) -} diff --git a/pruner/light/pruner.go b/pruner/light/pruner.go deleted file mode 100644 index e6c8a6601b..0000000000 --- a/pruner/light/pruner.go +++ /dev/null @@ -1,46 +0,0 @@ -package light - -import ( - "context" - - "github.com/ipfs/boxo/blockservice" - "github.com/ipfs/boxo/blockstore" - "github.com/ipfs/go-datastore" - - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/pruner" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -type Pruner struct { - bserv blockservice.BlockService - ds datastore.Datastore -} - -func NewPruner(bstore blockstore.Blockstore, ds datastore.Batching) pruner.Pruner { - return &Pruner{bserv: ipld.NewBlockservice(bstore, nil), ds: ds} -} - -func (p *Pruner) Prune(ctx context.Context, h *header.ExtendedHeader) error { - dah := h.DAH - if share.DataHash(dah.Hash()).IsEmptyRoot() { - return nil - } - - var roots [][]byte - roots = append(roots, h.DAH.RowRoots...) - roots = append(roots, h.DAH.ColumnRoots...) - for _, root := range roots { - cid := ipld.MustCidFromNamespacedSha256(root) - if err := ipld.DeleteNode(ctx, p.bserv, cid); err != nil { - return err - } - } - - return p.ds.Delete(ctx, rootKey(dah)) -} - -func rootKey(root *share.Root) datastore.Key { - return datastore.NewKey(root.String()) -} diff --git a/pruner/light/window.go b/pruner/light/window.go deleted file mode 100644 index eeabb0fda7..0000000000 --- a/pruner/light/window.go +++ /dev/null @@ -1,11 +0,0 @@ -package light - -import ( - "time" - - "github.com/celestiaorg/celestia-node/pruner" -) - -// Window is the availability window for light nodes in the Celestia -// network (30 days). -const Window = pruner.AvailabilityWindow(30 * 24 * time.Hour) diff --git a/pruner/light/window_test.go b/pruner/light/window_test.go deleted file mode 100644 index 764be542fd..0000000000 --- a/pruner/light/window_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package light - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -// TestLightWindowConst exists to ensure that any changes to the sampling window -// are deliberate. -func TestLightWindowConst(t *testing.T) { - assert.Equal(t, Window.Duration(), 30*24*time.Hour) -} diff --git a/pruner/service.go b/pruner/service.go index 8ba6ccfc24..7281f454aa 100644 --- a/pruner/service.go +++ b/pruner/service.go @@ -19,7 +19,7 @@ var log = logging.Logger("pruner/service") // Service handles running the pruning cycle for the node. type Service struct { pruner Pruner - window AvailabilityWindow + window time.Duration getter libhead.Getter[*header.ExtendedHeader] @@ -38,7 +38,7 @@ type Service struct { func NewService( p Pruner, - window AvailabilityWindow, + window time.Duration, getter libhead.Getter[*header.ExtendedHeader], ds datastore.Datastore, blockTime time.Duration, @@ -94,6 +94,19 @@ func (s *Service) Stop(ctx context.Context) error { } } +func (s *Service) LastPruned(ctx context.Context) (uint64, error) { + err := s.loadCheckpoint(ctx) + if err != nil { + return 0, err + } + return s.checkpoint.LastPrunedHeight, nil +} + +func (s *Service) ResetCheckpoint(ctx context.Context) error { + s.checkpoint = newCheckpoint() + return storeCheckpoint(ctx, s.ds, s.checkpoint) +} + // run prunes blocks older than the availability wiindow periodically until the // pruner service is stopped. func (s *Service) run() { diff --git a/pruner/service_test.go b/pruner/service_test.go index c1be1bc74e..700951cf65 100644 --- a/pruner/service_test.go +++ b/pruner/service_test.go @@ -40,7 +40,7 @@ func TestService(t *testing.T) { serv, err := NewService( mp, - AvailabilityWindow(time.Millisecond*2), + time.Millisecond*2, store, sync.MutexWrap(datastore.NewMapDatastore()), blockTime, @@ -82,7 +82,7 @@ func TestService_FailedAreRecorded(t *testing.T) { serv, err := NewService( mp, - AvailabilityWindow(time.Millisecond*20), + time.Millisecond*20, store, sync.MutexWrap(datastore.NewMapDatastore()), blockTime, @@ -127,7 +127,7 @@ func TestServiceCheckpointing(t *testing.T) { serv, err := NewService( mp, - AvailabilityWindow(time.Second), + time.Second, store, sync.MutexWrap(datastore.NewMapDatastore()), time.Millisecond, @@ -164,7 +164,7 @@ func TestPrune_LargeNumberOfBlocks(t *testing.T) { t.Cleanup(func() { maxHeadersPerLoop = maxHeadersPerLoopOld }) blockTime := time.Nanosecond - availabilityWindow := AvailabilityWindow(blockTime * 10) + availabilityWindow := blockTime * 10 // all headers generated in suite are timestamped to time.Now(), so // they will all be considered "pruneable" within the availability window @@ -187,7 +187,7 @@ func TestPrune_LargeNumberOfBlocks(t *testing.T) { require.NoError(t, err) // ensures availability window has passed - time.Sleep(availabilityWindow.Duration() + time.Millisecond*100) + time.Sleep(availabilityWindow + time.Millisecond*100) // trigger a prune job lastPruned, err := serv.lastPruned(ctx) @@ -202,7 +202,7 @@ func TestPrune_LargeNumberOfBlocks(t *testing.T) { func TestFindPruneableHeaders(t *testing.T) { testCases := []struct { name string - availWindow AvailabilityWindow + availWindow time.Duration blockTime time.Duration startTime time.Time headerAmount int @@ -211,7 +211,7 @@ func TestFindPruneableHeaders(t *testing.T) { { name: "Estimated range matches expected", // Availability window is one week - availWindow: AvailabilityWindow(time.Hour * 24 * 7), + availWindow: time.Hour * 24 * 7, blockTime: time.Hour, // Make two weeks of headers headerAmount: 2 * (24 * 7), @@ -222,7 +222,7 @@ func TestFindPruneableHeaders(t *testing.T) { { name: "Estimated range not sufficient but finds the correct tail", // Availability window is one week - availWindow: AvailabilityWindow(time.Hour * 24 * 7), + availWindow: time.Hour * 24 * 7, blockTime: time.Hour, // Make three weeks of headers headerAmount: 3 * (24 * 7), @@ -233,7 +233,7 @@ func TestFindPruneableHeaders(t *testing.T) { { name: "No pruneable headers", // Availability window is two weeks - availWindow: AvailabilityWindow(2 * time.Hour * 24 * 7), + availWindow: 2 * time.Hour * 24 * 7, blockTime: time.Hour, // Make one week of headers headerAmount: 24 * 7, @@ -248,8 +248,8 @@ func TestFindPruneableHeaders(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - headerGenerator := NewSpacedHeaderGenerator(t, tc.startTime, tc.blockTime) - store := headertest.NewCustomStore(t, headerGenerator, tc.headerAmount) + suite := headertest.NewTestSuiteWithGenesisTime(t, tc.startTime, tc.blockTime) + store := headertest.NewCustomStore(t, suite, tc.headerAmount) mp := &mockPruner{} @@ -272,7 +272,7 @@ func TestFindPruneableHeaders(t *testing.T) { require.NoError(t, err) require.Len(t, pruneable, tc.expectedLength) - pruneableCutoff := time.Now().Add(-tc.availWindow.Duration()) + pruneableCutoff := time.Now().Add(-tc.availWindow) // All returned headers are older than the availability window for _, h := range pruneable { require.WithinRange(t, h.Time(), tc.startTime, pruneableCutoff) @@ -291,6 +291,37 @@ func TestFindPruneableHeaders(t *testing.T) { } } +func TestService_ClearCheckpoint(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + serv, err := NewService( + &mockPruner{}, + time.Second, // doesn't matter + headertest.NewStore(t), + sync.MutexWrap(datastore.NewMapDatastore()), + time.Second, // doesn't matter + ) + require.NoError(t, err) + serv.ctx = ctx + + oldCp := &checkpoint{LastPrunedHeight: 500, FailedHeaders: map[uint64]struct{}{4: {}}} + err = storeCheckpoint(ctx, serv.ds, oldCp) + require.NoError(t, err) + + err = serv.loadCheckpoint(ctx) + require.NoError(t, err) + assert.Equal(t, oldCp, serv.checkpoint) + + err = serv.ResetCheckpoint(ctx) + require.NoError(t, err) + + err = serv.loadCheckpoint(ctx) + require.NoError(t, err) + + assert.Equal(t, newCheckpoint(), serv.checkpoint) +} + type mockPruner struct { deletedHeaderHashes []pruned @@ -317,32 +348,3 @@ func (mp *mockPruner) Prune(_ context.Context, h *header.ExtendedHeader) error { mp.deletedHeaderHashes = append(mp.deletedHeaderHashes, pruned{hash: h.Hash().String(), height: h.Height()}) return nil } - -// TODO @renaynay @distractedm1nd: Deduplicate via headertest utility. -// https://github.com/celestiaorg/celestia-node/issues/3278. -type SpacedHeaderGenerator struct { - t *testing.T - TimeBetweenHeaders time.Duration - currentTime time.Time - currentHeight int64 -} - -func NewSpacedHeaderGenerator( - t *testing.T, startTime time.Time, timeBetweenHeaders time.Duration, -) *SpacedHeaderGenerator { - return &SpacedHeaderGenerator{ - t: t, - TimeBetweenHeaders: timeBetweenHeaders, - currentTime: startTime, - currentHeight: 1, - } -} - -func (shg *SpacedHeaderGenerator) NextHeader() *header.ExtendedHeader { - h := headertest.RandExtendedHeaderAtTimestamp(shg.t, shg.currentTime) - h.RawHeader.Height = shg.currentHeight - h.RawHeader.Time = shg.currentTime - shg.currentHeight++ - shg.currentTime = shg.currentTime.Add(shg.TimeBetweenHeaders) - return h -} diff --git a/pruner/window.go b/pruner/window.go deleted file mode 100644 index 4d7ba5c776..0000000000 --- a/pruner/window.go +++ /dev/null @@ -1,21 +0,0 @@ -package pruner - -import ( - "time" -) - -type AvailabilityWindow time.Duration - -func (aw AvailabilityWindow) Duration() time.Duration { - return time.Duration(aw) -} - -// IsWithinAvailabilityWindow checks whether the given timestamp is within the -// given AvailabilityWindow. If the window is disabled (0), it returns true for -// every timestamp. -func IsWithinAvailabilityWindow(t time.Time, window AvailabilityWindow) bool { - if window.Duration() == time.Duration(0) { - return true - } - return time.Since(t) <= window.Duration() -} diff --git a/share/availability.go b/share/availability.go index 84ae08bc53..3373a62276 100644 --- a/share/availability.go +++ b/share/availability.go @@ -4,29 +4,12 @@ import ( "context" "errors" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/rsmt2d" - "github.com/celestiaorg/celestia-node/header" ) // ErrNotAvailable is returned whenever DA sampling fails. var ErrNotAvailable = errors.New("share: data not available") -// Root represents root commitment to multiple Shares. -// In practice, it is a commitment to all the Data in a square. -type Root = da.DataAvailabilityHeader - -// NewRoot generates Root(DataAvailabilityHeader) using the -// provided extended data square. -func NewRoot(eds *rsmt2d.ExtendedDataSquare) (*Root, error) { - dah, err := da.NewDataAvailabilityHeader(eds) - if err != nil { - return nil, err - } - return &dah, nil -} - // Availability defines interface for validation of Shares' availability. // //go:generate mockgen -destination=availability/mocks/availability.go -package=mocks . Availability diff --git a/share/availability/full/availability.go b/share/availability/full/availability.go index 573819dc9f..16d57ea4df 100644 --- a/share/availability/full/availability.go +++ b/share/availability/full/availability.go @@ -4,35 +4,53 @@ import ( "context" "errors" "fmt" + "time" - "github.com/filecoin-project/dagstore" logging "github.com/ipfs/go-log/v2" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/availability" "github.com/celestiaorg/celestia-node/share/eds/byzantine" - "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/store" ) +// ErrDisallowRevertToArchival is returned when a node has been run with pruner enabled before and +// launching it with archival mode. +var ErrDisallowRevertToArchival = errors.New( + "node has been run with pruner enabled before, it is not safe to convert to an archival" + + "Run with --experimental-pruning enabled or consider re-initializing the store") + var log = logging.Logger("share/full") // ShareAvailability implements share.Availability using the full data square // recovery technique. It is considered "full" because it is required // to download enough shares to fully reconstruct the data square. type ShareAvailability struct { - store *eds.Store - getter share.Getter + store *store.Store + getter shwap.Getter + + storageWindow time.Duration + archival bool } // NewShareAvailability creates a new full ShareAvailability. func NewShareAvailability( - store *eds.Store, - getter share.Getter, + store *store.Store, + getter shwap.Getter, + opts ...Option, ) *ShareAvailability { + p := defaultParams() + for _, opt := range opts { + opt(p) + } + return &ShareAvailability{ - store: store, - getter: getter, + store: store, + getter: getter, + storageWindow: availability.StorageWindow, + archival: p.archival, } } @@ -40,28 +58,30 @@ func NewShareAvailability( // enough Shares from the network. func (fa *ShareAvailability) SharesAvailable(ctx context.Context, header *header.ExtendedHeader) error { dah := header.DAH - // short-circuit if the given root is minimum DAH of an empty data square, to avoid datastore hit - if share.DataHash(dah.Hash()).IsEmptyRoot() { - return nil + + if !fa.archival { + // do not sync blocks outside of sampling window if not archival + if !availability.IsWithinWindow(header.Time(), fa.storageWindow) { + log.Debugw("skipping availability check for block outside sampling"+ + " window", "height", header.Height(), "data hash", dah.String()) + return availability.ErrOutsideSamplingWindow + } } - // we assume the caller of this method has already performed basic validation on the - // given dah/root. If for some reason this has not happened, the node should panic. - if err := dah.ValidateBasic(); err != nil { - log.Errorw("Availability validation cannot be performed on a malformed DataAvailabilityHeader", - "err", err) - panic(err) + // if the data square is empty, we can safely link the header height in the store to an empty EDS. + if share.DataHash(dah.Hash()).IsEmptyEDS() { + err := fa.store.PutODSQ4(ctx, dah, header.Height(), share.EmptyEDS()) + if err != nil { + return fmt.Errorf("put empty EDS: %w", err) + } + return nil } // a hack to avoid loading the whole EDS in mem if we store it already. - if ok, _ := fa.store.Has(ctx, dah.Hash()); ok { + if ok, _ := fa.store.HasByHeight(ctx, header.Height()); ok { return nil } - adder := ipld.NewProofsAdder(len(dah.RowRoots)) - ctx = ipld.CtxWithProofsAdder(ctx, adder) - defer adder.Purge() - eds, err := fa.getter.GetEDS(ctx, header) if err != nil { if errors.Is(err, context.Canceled) { @@ -69,15 +89,31 @@ func (fa *ShareAvailability) SharesAvailable(ctx context.Context, header *header } log.Errorw("availability validation failed", "root", dah.String(), "err", err.Error()) var byzantineErr *byzantine.ErrByzantine - if errors.Is(err, share.ErrNotFound) || errors.Is(err, context.DeadlineExceeded) && !errors.As(err, &byzantineErr) { + if errors.Is(err, shwap.ErrNotFound) || errors.Is(err, context.DeadlineExceeded) && !errors.As(err, &byzantineErr) { return share.ErrNotAvailable } return err } - err = fa.store.Put(ctx, dah.Hash(), eds) - if err != nil && !errors.Is(err, dagstore.ErrShardExists) { + // archival nodes should not store Q4 outside the availability window. + if availability.IsWithinWindow(header.Time(), fa.storageWindow) { + err = fa.store.PutODSQ4(ctx, dah, header.Height(), eds) + } else { + err = fa.store.PutODS(ctx, dah, header.Height(), eds) + } + + if err != nil { return fmt.Errorf("full availability: failed to store eds: %w", err) } return nil } + +func (fa *ShareAvailability) Prune(ctx context.Context, eh *header.ExtendedHeader) error { + if fa.archival { + log.Debugf("trimming Q4 from block %s at height %d", eh.DAH.String(), eh.Height()) + return fa.store.RemoveQ4(ctx, eh.Height(), eh.DAH.Hash()) + } + + log.Debugf("removing block %s at height %d", eh.DAH.String(), eh.Height()) + return fa.store.RemoveODSQ4(ctx, eh.Height(), eh.DAH.Hash()) +} diff --git a/share/availability/full/availability_test.go b/share/availability/full/availability_test.go index 400acfa087..24435ee0d8 100644 --- a/share/availability/full/availability_test.go +++ b/share/availability/full/availability_test.go @@ -3,80 +3,149 @@ package full import ( "context" "testing" + "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/celestia-node/header/headertest" "github.com/celestiaorg/celestia-node/share" - availability_test "github.com/celestiaorg/celestia-node/share/availability/test" + "github.com/celestiaorg/celestia-node/share/availability" "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/mocks" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/getters/mock" + "github.com/celestiaorg/celestia-node/store" ) -func TestShareAvailableOverMocknet_Full(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) +func TestSharesAvailable(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - net := availability_test.NewTestDAGNet(ctx, t) - _, root := RandNode(net, 32) + // RandServiceWithSquare creates a NewShareAvailability inside, so we can test it + eds := edstest.RandEDS(t, 16) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) - eh := headertest.RandExtendedHeaderWithRoot(t, root) - nd := Node(net) - net.ConnectAll() + getter := mock.NewMockGetter(gomock.NewController(t)) + getter.EXPECT().GetEDS(gomock.Any(), eh).Return(eds, nil) - err := nd.SharesAvailable(ctx, eh) - assert.NoError(t, err) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) + avail := NewShareAvailability(store, getter) + err = avail.SharesAvailable(ctx, eh) + require.NoError(t, err) + + // Check if the store has the root + has, err := store.HasByHash(ctx, roots.Hash()) + require.NoError(t, err) + require.True(t, has) + + // Check if the store has the root linked to the height + has, err = store.HasByHeight(ctx, eh.Height()) + require.NoError(t, err) + require.True(t, has) } -func TestSharesAvailable_Full(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) +func TestSharesAvailable_StoredEds(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - // RandServiceWithSquare creates a NewShareAvailability inside, so we can test it - getter, dah := GetterWithRandSquare(t, 16) + eds := edstest.RandEDS(t, 4) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + require.NoError(t, err) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - avail := TestAvailability(t, getter) - err := avail.SharesAvailable(ctx, eh) - assert.NoError(t, err) -} + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) + avail := NewShareAvailability(store, nil) -func TestSharesAvailable_StoresToEDSStore(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + err = store.PutODSQ4(ctx, roots, eh.Height(), eds) + require.NoError(t, err) - // RandServiceWithSquare creates a NewShareAvailability inside, so we can test it - getter, dah := GetterWithRandSquare(t, 16) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - avail := TestAvailability(t, getter) - err := avail.SharesAvailable(ctx, eh) - assert.NoError(t, err) - - has, err := avail.store.Has(ctx, dah.Hash()) - assert.NoError(t, err) - assert.True(t, has) + has, err := store.HasByHeight(ctx, eh.Height()) + require.NoError(t, err) + require.True(t, has) + + err = avail.SharesAvailable(ctx, eh) + require.NoError(t, err) + + has, err = store.HasByHeight(ctx, eh.Height()) + require.NoError(t, err) + require.True(t, has) } -func TestSharesAvailable_Full_ErrNotAvailable(t *testing.T) { +func TestSharesAvailable_ErrNotAvailable(t *testing.T) { ctrl := gomock.NewController(t) - getter := mocks.NewMockGetter(ctrl) - ctx, cancel := context.WithCancel(context.Background()) + getter := mock.NewMockGetter(ctrl) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() eds := edstest.RandEDS(t, 4) - dah, err := da.NewDataAvailabilityHeader(eds) - eh := headertest.RandExtendedHeaderWithRoot(t, &dah) + roots, err := share.NewAxisRoots(eds) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) require.NoError(t, err) - avail := TestAvailability(t, getter) - errors := []error{share.ErrNotFound, context.DeadlineExceeded} + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) + avail := NewShareAvailability(store, getter) + + errors := []error{shwap.ErrNotFound, context.DeadlineExceeded} for _, getterErr := range errors { getter.EXPECT().GetEDS(gomock.Any(), gomock.Any()).Return(nil, getterErr) err := avail.SharesAvailable(ctx, eh) require.ErrorIs(t, err, share.ErrNotAvailable) } } + +// TestSharesAvailable_OutsideSamplingWindow_NonArchival tests to make sure +// blocks are skipped that are outside sampling window. +func TestSharesAvailable_OutsideSamplingWindow_NonArchival(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + getter := mock.NewMockGetter(gomock.NewController(t)) + getter.EXPECT().GetEDS(gomock.Any(), gomock.Any()).Times(0) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) + + suite := headertest.NewTestSuite(t, 3, time.Nanosecond) + headers := suite.GenExtendedHeaders(10) + + avail := NewShareAvailability(store, getter) + avail.storageWindow = time.Nanosecond // make all headers outside sampling window + + for _, h := range headers { + err := avail.SharesAvailable(ctx, h) + assert.ErrorIs(t, err, availability.ErrOutsideSamplingWindow) + } +} + +// TestSharesAvailable_OutsideSamplingWindow_Archival tests to make sure +// blocks are still synced that are outside sampling window. +func TestSharesAvailable_OutsideSamplingWindow_Archival(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + getter := mock.NewMockGetter(gomock.NewController(t)) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) + + eds := edstest.RandEDS(t, 4) + roots, err := share.NewAxisRoots(eds) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + require.NoError(t, err) + + getter.EXPECT().GetEDS(gomock.Any(), gomock.Any()).Times(1).Return(eds, nil) + + avail := NewShareAvailability(store, getter, WithArchivalMode()) + avail.storageWindow = time.Nanosecond // make all headers outside sampling window + + err = avail.SharesAvailable(ctx, eh) + require.NoError(t, err) + has, err := store.HasByHash(ctx, roots.Hash()) + require.NoError(t, err) + assert.True(t, has) +} diff --git a/share/availability/full/options.go b/share/availability/full/options.go new file mode 100644 index 0000000000..689a225a9a --- /dev/null +++ b/share/availability/full/options.go @@ -0,0 +1,25 @@ +package full + +type params struct { + archival bool +} + +// Option is a function that configures light availability Parameters +type Option func(*params) + +// DefaultParameters returns the default Parameters' configuration values +// for the light availability implementation +func defaultParams() *params { + return ¶ms{ + archival: false, + } +} + +// WithArchivalMode is a functional option to tell the full availability +// implementation that the node wants to sync *all* blocks, not just those +// within the sampling window. +func WithArchivalMode() Option { + return func(p *params) { + p.archival = true + } +} diff --git a/share/availability/full/reconstruction_test.go b/share/availability/full/reconstruction_test.go index 31edb3b6d9..179a458d36 100644 --- a/share/availability/full/reconstruction_test.go +++ b/share/availability/full/reconstruction_test.go @@ -1,284 +1,284 @@ -//go:build !race - +// //go:build !race package full -import ( - "context" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/require" - "golang.org/x/sync/errgroup" - - "github.com/celestiaorg/celestia-node/header/headertest" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/availability/light" - availability_test "github.com/celestiaorg/celestia-node/share/availability/test" - "github.com/celestiaorg/celestia-node/share/eds" -) - -func init() { - eds.RetrieveQuadrantTimeout = time.Millisecond * 100 // to speed up tests -} - -// TestShareAvailable_OneFullNode asserts that a full node can ensure -// data is available (reconstruct data square) while being connected to -// light nodes only. -func TestShareAvailable_OneFullNode(t *testing.T) { - // NOTE: Numbers are taken from the original 'Fraud and Data Availability Proofs' paper - light.DefaultSampleAmount = 20 // s - const ( - origSquareSize = 16 // k - lightNodes = 69 // c - ) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - - net := availability_test.NewTestDAGNet(ctx, t) - source, root := RandNode(net, origSquareSize) // make a source node, a.k.a bridge - eh := headertest.RandExtendedHeader(t) - eh.DAH = root - full := Node(net) // make a full availability service which reconstructs data - - // ensure there is no connection between source and full nodes - // so that full reconstructs from the light nodes only - net.Disconnect(source.ID(), full.ID()) - - errg, errCtx := errgroup.WithContext(ctx) - errg.Go(func() error { - return full.SharesAvailable(errCtx, eh) - }) - - lights := make([]*availability_test.TestNode, lightNodes) - for i := 0; i < len(lights); i++ { - lights[i] = light.Node(net) - go func(i int) { - err := lights[i].SharesAvailable(ctx, eh) - if err != nil { - t.Log("light errors:", err) - } - }(i) - } - - for i := 0; i < len(lights); i++ { - net.Connect(lights[i].ID(), source.ID()) - } - - for i := 0; i < len(lights); i++ { - net.Connect(lights[i].ID(), full.ID()) - } - - err := errg.Wait() - require.NoError(t, err) -} - -// TestShareAvailable_ConnectedFullNodes asserts that two connected full nodes -// can ensure data availability via two isolated light node subnetworks. Full -// nodes start their availability process first, then light node start -// availability process and connect to full node and only after light node -// connect to the source node which has the data. After light node connect to the -// source, full node must be able to finish the availability process started in -// the beginning. -func TestShareAvailable_ConnectedFullNodes(t *testing.T) { - // NOTE: Numbers are taken from the original 'Fraud and Data Availability Proofs' paper - light.DefaultSampleAmount = 20 // s - const ( - origSquareSize = 16 // k - lightNodes = 60 // c - ) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - net := availability_test.NewTestDAGNet(ctx, t) - source, root := RandNode(net, origSquareSize) - eh := headertest.RandExtendedHeader(t) - eh.DAH = root - - // create two full nodes and ensure they are disconnected - full1 := Node(net) - full2 := Node(net) - - // pre-connect fulls - net.Connect(full1.ID(), full2.ID()) - // ensure fulls and source are not connected - // so that fulls take data from light nodes only - net.Disconnect(full1.ID(), source.ID()) - net.Disconnect(full2.ID(), source.ID()) - - // start reconstruction for fulls - errg, errCtx := errgroup.WithContext(ctx) - errg.Go(func() error { - return full1.SharesAvailable(errCtx, eh) - }) - errg.Go(func() error { - return full2.SharesAvailable(errCtx, eh) - }) - - // create light nodes and start sampling for them immediately - lights1, lights2 := make( - []*availability_test.TestNode, lightNodes/2), - make([]*availability_test.TestNode, lightNodes/2) - for i := 0; i < len(lights1); i++ { - lights1[i] = light.Node(net) - go func(i int) { - err := lights1[i].SharesAvailable(ctx, eh) - if err != nil { - t.Log("light1 errors:", err) - } - }(i) - - lights2[i] = light.Node(net) - go func(i int) { - err := lights2[i].SharesAvailable(ctx, eh) - if err != nil { - t.Log("light2 errors:", err) - } - }(i) - } - - // shape topology - for i := 0; i < len(lights1); i++ { - // ensure lights1 are only connected to full1 - net.Connect(lights1[i].ID(), full1.ID()) - net.Disconnect(lights1[i].ID(), full2.ID()) - // ensure lights2 are only connected to full2 - net.Connect(lights2[i].ID(), full2.ID()) - net.Disconnect(lights2[i].ID(), full1.ID()) - } - - // start connection lights with sources - for i := 0; i < len(lights1); i++ { - net.Connect(lights1[i].ID(), source.ID()) - net.Connect(lights2[i].ID(), source.ID()) - } - - err := errg.Wait() - require.NoError(t, err) -} - -// TestShareAvailable_DisconnectedFullNodes asserts that two disconnected full -// nodes cannot ensure data is available (reconstruct data square) while being -// connected to isolated light nodes subnetworks, which do not have enough nodes -// to reconstruct the data, but once ShareAvailability nodes connect, they can -// collectively reconstruct it. -// -//nolint:dupword -func TestShareAvailable_DisconnectedFullNodes(t *testing.T) { - // S - Source - // L - Light Node - // F - Full Node - // ── - connection - // - // Topology: - // NOTE: There are more Light Nodes in practice - // ┌─┬─┬─S─┬─┬─┐ - // │ │ │ │ │ │ - // │ │ │ │ │ │ - // │ │ │ │ │ │ - // L L L L L L - // │ │ │ │ │ │ - // └─┴─┤ ├─┴─┘ - // F└───┘F - // - - // NOTE: Numbers are taken from the original 'Fraud and Data Availability Proofs' paper - light.DefaultSampleAmount = 20 // s - const ( - origSquareSize = 16 // k - lightNodes = 32 // c - total number of nodes on two subnetworks - ) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) - defer cancel() - - net := availability_test.NewTestDAGNet(ctx, t) - source, root := RandNode(net, origSquareSize) - eh := headertest.RandExtendedHeader(t) - eh.DAH = root - - // create light nodes and start sampling for them immediately - lights1, lights2 := make( - []*availability_test.TestNode, lightNodes/2), - make([]*availability_test.TestNode, lightNodes/2) - - var wg sync.WaitGroup - wg.Add(lightNodes) - for i := 0; i < len(lights1); i++ { - lights1[i] = light.Node(net) - go func(i int) { - defer wg.Done() - err := lights1[i].SharesAvailable(ctx, eh) - if err != nil { - t.Log("light1 errors:", err) - } - }(i) - - lights2[i] = light.Node(net) - go func(i int) { - defer wg.Done() - err := lights2[i].SharesAvailable(ctx, eh) - if err != nil { - t.Log("light2 errors:", err) - } - }(i) - } - - // create two full nodes and ensure they are disconnected - full1 := Node(net) - full2 := Node(net) - net.Disconnect(full1.ID(), full2.ID()) - - // ensure fulls and source are not connected - // so that fulls take data from light nodes only - net.Disconnect(full1.ID(), source.ID()) - net.Disconnect(full2.ID(), source.ID()) - - // shape topology - for i := 0; i < len(lights1); i++ { - // ensure lights1 are only connected to source and full1 - net.Connect(lights1[i].ID(), source.ID()) - net.Connect(lights1[i].ID(), full1.ID()) - net.Disconnect(lights1[i].ID(), full2.ID()) - // ensure lights2 are only connected to source and full2 - net.Connect(lights2[i].ID(), source.ID()) - net.Connect(lights2[i].ID(), full2.ID()) - net.Disconnect(lights2[i].ID(), full1.ID()) - } - - // start reconstruction for fulls that should fail - ctxErr, cancelErr := context.WithTimeout(ctx, time.Second*5) - errg, errCtx := errgroup.WithContext(ctxErr) - errg.Go(func() error { - return full1.SharesAvailable(errCtx, eh) - }) - errg.Go(func() error { - return full2.SharesAvailable(errCtx, eh) - }) - - // check that any of the fulls cannot reconstruct on their own - err := errg.Wait() - require.ErrorIs(t, err, share.ErrNotAvailable) - cancelErr() - - // but after they connect - net.Connect(full1.ID(), full2.ID()) - - // with clean caches from the previous try - full1.ClearStorage() - full2.ClearStorage() - - // they both should be able to reconstruct the block - errg, bctx := errgroup.WithContext(ctx) - errg.Go(func() error { - return full1.SharesAvailable(bctx, eh) - }) - errg.Go(func() error { - return full2.SharesAvailable(bctx, eh) - }) - require.NoError(t, errg.Wait()) - // wait for all routines to finish before exit, in case there are any errors to log - wg.Wait() -} +// +// import ( +// "context" +// "sync" +// "testing" +// "time" +// +// "github.com/stretchr/testify/require" +// "golang.org/x/sync/errgroup" +// +// "github.com/celestiaorg/celestia-node/header/headertest" +// "github.com/celestiaorg/celestia-node/share" +// "github.com/celestiaorg/celestia-node/share/availability/light" +// availability_test "github.com/celestiaorg/celestia-node/share/availability/test" +// "github.com/celestiaorg/celestia-node/share/eds" +//) +// +// func init() { +// eds.RetrieveQuadrantTimeout = time.Millisecond * 100 // to speed up tests +//} +// +//// TestShareAvailable_OneFullNode asserts that a full node can ensure +//// data is available (reconstruct data square) while being connected to +//// light nodes only. +// func TestShareAvailable_OneFullNode(t *testing.T) { +// // NOTE: Numbers are taken from the original 'Fraud and Data Availability Proofs' paper +// light.DefaultSampleAmount = 20 // s +// const ( +// origSquareSize = 16 // k +// lightNodes = 69 // c +// ) +// +// ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) +// defer cancel() +// +// net := availability_test.NewTestDAGNet(ctx, t) +// source, root := RandNode(net, origSquareSize) // make a source node, a.k.a bridge +// eh := headertest.RandExtendedHeader(t) +// eh.DAH = root +// full := Node(net) // make a full availability service which reconstructs data +// +// // ensure there is no connection between source and full nodes +// // so that full reconstructs from the light nodes only +// net.Disconnect(source.ID(), full.ID()) +// +// errg, errCtx := errgroup.WithContext(ctx) +// errg.Go(func() error { +// return full.SharesAvailable(errCtx, eh) +// }) +// +// lights := make([]*availability_test.TestNode, lightNodes) +// for i := 0; i < len(lights); i++ { +// lights[i] = light.Node(net) +// go func(i int) { +// err := lights[i].SharesAvailable(ctx, eh) +// if err != nil { +// t.Log("light errors:", err) +// } +// }(i) +// } +// +// for i := 0; i < len(lights); i++ { +// net.Connect(lights[i].ID(), source.ID()) +// } +// +// for i := 0; i < len(lights); i++ { +// net.Connect(lights[i].ID(), full.ID()) +// } +// +// err := errg.Wait() +// require.NoError(t, err) +//} +// +//// TestShareAvailable_ConnectedFullNodes asserts that two connected full nodes +//// can ensure data availability via two isolated light node subnetworks. Full +//// nodes start their availability process first, then light node start +//// availability process and connect to full node and only after light node +//// connect to the source node which has the data. After light node connect to the +//// source, full node must be able to finish the availability process started in +//// the beginning. +// func TestShareAvailable_ConnectedFullNodes(t *testing.T) { +// // NOTE: Numbers are taken from the original 'Fraud and Data Availability Proofs' paper +// light.DefaultSampleAmount = 20 // s +// const ( +// origSquareSize = 16 // k +// lightNodes = 60 // c +// ) +// +// ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) +// defer cancel() +// +// net := availability_test.NewTestDAGNet(ctx, t) +// source, root := RandNode(net, origSquareSize) +// eh := headertest.RandExtendedHeader(t) +// eh.DAH = root +// +// // create two full nodes and ensure they are disconnected +// full1 := Node(net) +// full2 := Node(net) +// +// // pre-connect fulls +// net.Connect(full1.ID(), full2.ID()) +// // ensure fulls and source are not connected +// // so that fulls take data from light nodes only +// net.Disconnect(full1.ID(), source.ID()) +// net.Disconnect(full2.ID(), source.ID()) +// +// // start reconstruction for fulls +// errg, errCtx := errgroup.WithContext(ctx) +// errg.Go(func() error { +// return full1.SharesAvailable(errCtx, eh) +// }) +// errg.Go(func() error { +// return full2.SharesAvailable(errCtx, eh) +// }) +// +// // create light nodes and start sampling for them immediately +// lights1, lights2 := make( +// []*availability_test.TestNode, lightNodes/2), +// make([]*availability_test.TestNode, lightNodes/2) +// for i := 0; i < len(lights1); i++ { +// lights1[i] = light.Node(net) +// go func(i int) { +// err := lights1[i].SharesAvailable(ctx, eh) +// if err != nil { +// t.Log("light1 errors:", err) +// } +// }(i) +// +// lights2[i] = light.Node(net) +// go func(i int) { +// err := lights2[i].SharesAvailable(ctx, eh) +// if err != nil { +// t.Log("light2 errors:", err) +// } +// }(i) +// } +// +// // shape topology +// for i := 0; i < len(lights1); i++ { +// // ensure lights1 are only connected to full1 +// net.Connect(lights1[i].ID(), full1.ID()) +// net.Disconnect(lights1[i].ID(), full2.ID()) +// // ensure lights2 are only connected to full2 +// net.Connect(lights2[i].ID(), full2.ID()) +// net.Disconnect(lights2[i].ID(), full1.ID()) +// } +// +// // start connection lights with sources +// for i := 0; i < len(lights1); i++ { +// net.Connect(lights1[i].ID(), source.ID()) +// net.Connect(lights2[i].ID(), source.ID()) +// } +// +// err := errg.Wait() +// require.NoError(t, err) +//} +// +//// TestShareAvailable_DisconnectedFullNodes asserts that two disconnected full +//// nodes cannot ensure data is available (reconstruct data square) while being +//// connected to isolated light nodes subnetworks, which do not have enough nodes +//// to reconstruct the data, but once ShareAvailability nodes connect, they can +//// collectively reconstruct it. +//// +////nolint:dupword +// func TestShareAvailable_DisconnectedFullNodes(t *testing.T) { +// // S - Source +// // L - Light Node +// // F - Full Node +// // ── - connection +// // +// // Topology: +// // NOTE: There are more Light Nodes in practice +// // ┌─┬─┬─S─┬─┬─┐ +// // │ │ │ │ │ │ +// // │ │ │ │ │ │ +// // │ │ │ │ │ │ +// // L L L L L L +// // │ │ │ │ │ │ +// // └─┴─┤ ├─┴─┘ +// // F└───┘F +// // +// +// // NOTE: Numbers are taken from the original 'Fraud and Data Availability Proofs' paper +// light.DefaultSampleAmount = 20 // s +// const ( +// origSquareSize = 16 // k +// lightNodes = 32 // c - total number of nodes on two subnetworks +// ) +// +// ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) +// defer cancel() +// +// net := availability_test.NewTestDAGNet(ctx, t) +// source, root := RandNode(net, origSquareSize) +// eh := headertest.RandExtendedHeader(t) +// eh.DAH = root +// +// // create light nodes and start sampling for them immediately +// lights1, lights2 := make( +// []*availability_test.TestNode, lightNodes/2), +// make([]*availability_test.TestNode, lightNodes/2) +// +// var wg sync.WaitGroup +// wg.Add(lightNodes) +// for i := 0; i < len(lights1); i++ { +// lights1[i] = light.Node(net) +// go func(i int) { +// defer wg.Done() +// err := lights1[i].SharesAvailable(ctx, eh) +// if err != nil { +// t.Log("light1 errors:", err) +// } +// }(i) +// +// lights2[i] = light.Node(net) +// go func(i int) { +// defer wg.Done() +// err := lights2[i].SharesAvailable(ctx, eh) +// if err != nil { +// t.Log("light2 errors:", err) +// } +// }(i) +// } +// +// // create two full nodes and ensure they are disconnected +// full1 := Node(net) +// full2 := Node(net) +// net.Disconnect(full1.ID(), full2.ID()) +// +// // ensure fulls and source are not connected +// // so that fulls take data from light nodes only +// net.Disconnect(full1.ID(), source.ID()) +// net.Disconnect(full2.ID(), source.ID()) +// +// // shape topology +// for i := 0; i < len(lights1); i++ { +// // ensure lights1 are only connected to source and full1 +// net.Connect(lights1[i].ID(), source.ID()) +// net.Connect(lights1[i].ID(), full1.ID()) +// net.Disconnect(lights1[i].ID(), full2.ID()) +// // ensure lights2 are only connected to source and full2 +// net.Connect(lights2[i].ID(), source.ID()) +// net.Connect(lights2[i].ID(), full2.ID()) +// net.Disconnect(lights2[i].ID(), full1.ID()) +// } +// +// // start reconstruction for fulls that should fail +// ctxErr, cancelErr := context.WithTimeout(ctx, time.Second*5) +// errg, errCtx := errgroup.WithContext(ctxErr) +// errg.Go(func() error { +// return full1.SharesAvailable(errCtx, eh) +// }) +// errg.Go(func() error { +// return full2.SharesAvailable(errCtx, eh) +// }) +// +// // check that any of the fulls cannot reconstruct on their own +// err := errg.Wait() +// require.ErrorIs(t, err, share.ErrNotAvailable) +// cancelErr() +// +// // but after they connect +// net.Connect(full1.ID(), full2.ID()) +// +// // with clean caches from the previous try +// full1.ClearStorage() +// full2.ClearStorage() +// +// // they both should be able to reconstruct the block +// errg, bctx := errgroup.WithContext(ctx) +// errg.Go(func() error { +// return full1.SharesAvailable(bctx, eh) +// }) +// errg.Go(func() error { +// return full2.SharesAvailable(bctx, eh) +// }) +// require.NoError(t, errg.Wait()) +// // wait for all routines to finish before exit, in case there are any errors to log +// wg.Wait() +//} diff --git a/share/availability/full/testing.go b/share/availability/full/testing.go index 7379c83441..626de5e3d6 100644 --- a/share/availability/full/testing.go +++ b/share/availability/full/testing.go @@ -1,56 +1,57 @@ package full -import ( - "context" - "testing" - "time" - - "github.com/ipfs/go-datastore" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-node/share" - availability_test "github.com/celestiaorg/celestia-node/share/availability/test" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/getters" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/p2p/discovery" -) - -// GetterWithRandSquare provides a share.Getter filled with 'n' NMT -// trees of 'n' random shares, essentially storing a whole square. -func GetterWithRandSquare(t *testing.T, n int) (share.Getter, *share.Root) { - bServ := ipld.NewMemBlockservice() - getter := getters.NewIPLDGetter(bServ) - return getter, availability_test.RandFillBS(t, n, bServ) -} - -// RandNode creates a Full Node filled with a random block of the given size. -func RandNode(dn *availability_test.TestDagNet, squareSize int) (*availability_test.TestNode, *share.Root) { - nd := Node(dn) - return nd, availability_test.RandFillBS(dn.T, squareSize, nd.BlockService) -} - -// Node creates a new empty Full Node. -func Node(dn *availability_test.TestDagNet) *availability_test.TestNode { - nd := dn.NewTestNode() - nd.Getter = getters.NewIPLDGetter(nd.BlockService) - nd.Availability = TestAvailability(dn.T, nd.Getter) - return nd -} - -func TestAvailability(t *testing.T, getter share.Getter) *ShareAvailability { - params := discovery.DefaultParameters() - params.AdvertiseInterval = time.Second - params.PeersLimit = 10 - - store, err := eds.NewStore(eds.DefaultParameters(), t.TempDir(), datastore.NewMapDatastore()) - require.NoError(t, err) - err = store.Start(context.Background()) - require.NoError(t, err) - - t.Cleanup(func() { - err = store.Stop(context.Background()) - require.NoError(t, err) - }) - return NewShareAvailability(store, getter) -} +// import ( +// "context" +// +// "testing" +// "time" +// +// "github.com/ipfs/go-datastore" +// "github.com/stretchr/testify/require" +// +// "github.com/celestiaorg/celestia-node/share" +// availability_test "github.com/celestiaorg/celestia-node/share/availability/test" +// "github.com/celestiaorg/celestia-node/share/eds" +// "github.com/celestiaorg/celestia-node/share/getters" +// "github.com/celestiaorg/celestia-node/share/ipld" +// "github.com/celestiaorg/celestia-node/share/p2p/discovery" +//) +// +//// GetterWithRandSquare provides a share.Getter filled with 'n' NMT +//// trees of 'n' random shares, essentially storing a whole share. +// func GetterWithRandSquare(t *testing.T, n int) (share.Getter, *share.AxisRoots) { +// bServ := ipld.NewMemBlockservice() +// getter := getters.NewIPLDGetter(bServ) +// return getter, availability_test.RandFillBS(t, n, bServ) +//} +// +//// RandNode creates a Full Node filled with a random block of the given size. +// func RandNode(dn *availability_test.TestDagNet, squareSize int) (*availability_test.TestNode, *share.AxisRoots) { +// nd := Node(dn) +// return nd, availability_test.RandFillBS(dn.T, squareSize, nd.BlockService) +//} +// +//// Node creates a new empty Full Node. +// func Node(dn *availability_test.TestDagNet) *availability_test.TestNode { +// nd := dn.NewTestNode() +// nd.Getter = getters.NewIPLDGetter(nd.BlockService) +// nd.Availability = TestAvailability(dn.T, nd.Getter) +// return nd +//} +// +// func TestAvailability(t *testing.T, getter share.Getter) *ShareAvailability { +// params := discovery.DefaultParameters() +// params.AdvertiseInterval = time.Second +// params.PeersLimit = 10 +// +// store, err := eds.NewStore(eds.DefaultParameters(), t.TempDir(), datastore.NewMapDatastore()) +// require.NoError(t, err) +// err = store.Start(context.Background()) +// require.NoError(t, err) +// +// t.Cleanup(func() { +// err = store.Stop(context.Background()) +// require.NoError(t, err) +// }) +// return NewShareAvailability(store, getter) +//} diff --git a/share/availability/full/utils.go b/share/availability/full/utils.go new file mode 100644 index 0000000000..a49c7f9be9 --- /dev/null +++ b/share/availability/full/utils.go @@ -0,0 +1,46 @@ +package full + +import ( + "bytes" + "context" + "fmt" + + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" +) + +var ( + storePrefix = datastore.NewKey("full_avail") + previousModeKey = datastore.NewKey("previous_mode") + pruned = []byte("pruned") + archival = []byte("archival") +) + +// ConvertFromArchivalToPruned ensures that a node has not been run with pruning enabled before +// cannot revert to archival mode. It returns true only if the node is converting to +// pruned mode for the first time. +func ConvertFromArchivalToPruned(ctx context.Context, ds datastore.Datastore, isArchival bool) (bool, error) { + ds = namespace.Wrap(ds, storePrefix) + + prevMode, err := ds.Get(ctx, previousModeKey) + if err != nil { + return false, err + } + + if bytes.Equal(prevMode, pruned) && isArchival { + return false, ErrDisallowRevertToArchival + } + + if bytes.Equal(prevMode, archival) && !isArchival { + // allow conversion from archival to pruned + err = ds.Put(ctx, previousModeKey, pruned) + if err != nil { + return false, fmt.Errorf("share/availability/full: failed to updated pruning mode in "+ + "datastore: %w", err) + } + return true, nil + } + + // no changes in pruning mode + return false, nil +} diff --git a/share/availability/full/utils_test.go b/share/availability/full/utils_test.go new file mode 100644 index 0000000000..8bdf3e509d --- /dev/null +++ b/share/availability/full/utils_test.go @@ -0,0 +1,72 @@ +package full + +import ( + "context" + "testing" + + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/namespace" + ds_sync "github.com/ipfs/go-datastore/sync" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestDisallowRevertArchival tests that a node that has been previously run +// with full pruning cannot convert back into an "archival" node +func TestDisallowRevertArchival(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + + // create a pruned node instance (non-archival) for the first time + nsWrapped := namespace.Wrap(ds, storePrefix) + err := nsWrapped.Put(ctx, previousModeKey, pruned) + require.NoError(t, err) + + convert, err := ConvertFromArchivalToPruned(ctx, ds, false) + assert.NoError(t, err) + assert.False(t, convert) + // ensure availability impl recorded the pruned run + prevMode, err := nsWrapped.Get(ctx, previousModeKey) + require.NoError(t, err) + assert.Equal(t, pruned, prevMode) + + // now change to archival mode, ensure failure + convert, err = ConvertFromArchivalToPruned(ctx, ds, true) + assert.Error(t, err) + assert.ErrorIs(t, err, ErrDisallowRevertToArchival) + assert.False(t, convert) + + // ensure the node can still run in pruned mode + convert, err = ConvertFromArchivalToPruned(ctx, ds, false) + assert.NoError(t, err) + assert.False(t, convert) +} + +// TestAllowConversionFromArchivalToPruned tests that a node that has been previously run +// in archival mode can convert to a pruned node +func TestAllowConversionFromArchivalToPruned(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + + // make an archival node + nsWrapped := namespace.Wrap(ds, storePrefix) + err := nsWrapped.Put(ctx, previousModeKey, archival) + require.NoError(t, err) + + convert, err := ConvertFromArchivalToPruned(ctx, ds, true) + assert.NoError(t, err) + assert.False(t, convert) + + // turn into a pruned node + convert, err = ConvertFromArchivalToPruned(ctx, ds, false) + assert.NoError(t, err) + assert.True(t, convert) + + prevMode, err := nsWrapped.Get(ctx, previousModeKey) + require.NoError(t, err) + assert.Equal(t, pruned, prevMode) +} diff --git a/share/availability/light/availability.go b/share/availability/light/availability.go index b188a33c14..f3afbb26fd 100644 --- a/share/availability/light/availability.go +++ b/share/availability/light/availability.go @@ -2,23 +2,31 @@ package light import ( "context" + "encoding/json" "errors" + "fmt" "sync" + "time" + "github.com/ipfs/boxo/blockstore" "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/autobatch" "github.com/ipfs/go-datastore/namespace" logging "github.com/ipfs/go-log/v2" "github.com/celestiaorg/celestia-node/header" + "github.com/celestiaorg/celestia-node/libs/utils" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/getters" + "github.com/celestiaorg/celestia-node/share/availability" + "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/bitswap" ) var ( - log = logging.Logger("share/light") - cacheAvailabilityPrefix = datastore.NewKey("sampling_result") - writeBatchSize = 2048 + log = logging.Logger("share/light") + samplingResultsPrefix = datastore.NewKey("sampling_result") + writeBatchSize = 2048 ) // ShareAvailability implements share.Availability using Data Availability Sampling technique. @@ -26,24 +34,26 @@ var ( // its availability. It is assumed that there are a lot of lightAvailability instances // on the network doing sampling over the same Root to collectively verify its availability. type ShareAvailability struct { - getter share.Getter + getter shwap.Getter + bs blockstore.Blockstore params Parameters - // TODO(@Wondertan): Once we come to parallelized DASer, this lock becomes a contention point - // Related to #483 - // TODO: Striped locks? :D - dsLk sync.RWMutex - ds *autobatch.Datastore + storageWindow time.Duration + + activeHeights *utils.Sessions + dsLk sync.RWMutex + ds *autobatch.Datastore } // NewShareAvailability creates a new light Availability. func NewShareAvailability( - getter share.Getter, + getter shwap.Getter, ds datastore.Batching, + bs blockstore.Blockstore, opts ...Option, ) *ShareAvailability { - params := DefaultParameters() - ds = namespace.Wrap(ds, cacheAvailabilityPrefix) + params := *DefaultParameters() + ds = namespace.Wrap(ds, samplingResultsPrefix) autoDS := autobatch.NewAutoBatching(ds, writeBatchSize) for _, opt := range opts { @@ -51,9 +61,12 @@ func NewShareAvailability( } return &ShareAvailability{ - getter: getter, - params: params, - ds: autoDS, + getter: getter, + bs: bs, + params: params, + storageWindow: availability.StorageWindow, + activeHeights: utils.NewSessions(), + ds: autoDS, } } @@ -61,103 +74,179 @@ func NewShareAvailability( // ExtendedHeader. This way SharesAvailable subjectively verifies that Shares are available. func (la *ShareAvailability) SharesAvailable(ctx context.Context, header *header.ExtendedHeader) error { dah := header.DAH - // short-circuit if the given root is minimum DAH of an empty data square - if share.DataHash(dah.Hash()).IsEmptyRoot() { + + // short-circuit if the given root is an empty data square + if share.DataHash(dah.Hash()).IsEmptyEDS() { return nil } - // load snapshot of the last sampling errors from disk - key := rootKey(dah) - la.dsLk.RLock() - last, err := la.ds.Get(ctx, key) - la.dsLk.RUnlock() + // short-circuit if outside sampling window + if !availability.IsWithinWindow(header.Time(), la.storageWindow) { + return availability.ErrOutsideSamplingWindow + } - // Check for error cases - var samples []Sample - switch { - case err == nil && len(last) == 0: - // Availability has already been validated - return nil - case err != nil && !errors.Is(err, datastore.ErrNotFound): - // Other error occurred + // Prevent multiple sampling and pruning sessions for the same header height + release, err := la.activeHeights.StartSession(ctx, header.Height()) + if err != nil { return err - case errors.Is(err, datastore.ErrNotFound): - // No sampling result found, select new samples - samples, err = SampleSquare(len(dah.RowRoots), int(la.params.SampleAmount)) - if err != nil { + } + defer release() + + key := datastoreKeyForRoot(dah) + samples := &SamplingResult{} + + // Attempt to load previous sampling results + la.dsLk.RLock() + data, err := la.ds.Get(ctx, key) + la.dsLk.RUnlock() + if err != nil { + if !errors.Is(err, datastore.ErrNotFound) { return err } - default: - // Sampling result found, unmarshal it - samples, err = decodeSamples(last) + // No previous results; create new samples + samples = NewSamplingResult(len(dah.RowRoots), int(la.params.SampleAmount)) + } else { + err = json.Unmarshal(data, samples) if err != nil { return err } + // Verify total samples count. + totalSamples := len(samples.Remaining) + len(samples.Available) + if (totalSamples != int(la.params.SampleAmount)) && (totalSamples != len(dah.RowRoots)*len(dah.RowRoots)) { + return fmt.Errorf("invalid sampling result:"+ + " expected %d samples, got %d", la.params.SampleAmount, totalSamples) + } } - if err := dah.ValidateBasic(); err != nil { - log.Errorw("DAH validation failed", "error", err) - return err + if len(samples.Remaining) == 0 { + // All samples have been processed successfully + return nil } - // indicate to the share.Getter that a blockservice session should be created. This - // functionality is optional and must be supported by the used share.Getter. - ctx = getters.WithSession(ctx) + log.Debugw("starting sampling session", "root", dah.String()) - var ( - failedSamplesLock sync.Mutex - failedSamples []Sample - ) + idxs := make([]shwap.SampleCoords, len(samples.Remaining)) + for i, s := range samples.Remaining { + idxs[i] = shwap.SampleCoords{Row: s.Row, Col: s.Col} + } - log.Debugw("starting sampling session", "root", dah.String()) - var wg sync.WaitGroup - for _, s := range samples { - wg.Add(1) - go func(s Sample) { - defer wg.Done() - // check if the sample is available - _, err := la.getter.GetShare(ctx, header, int(s.Row), int(s.Col)) - if err != nil { - log.Debugw("error fetching share", "root", dah.String(), "row", s.Row, "col", s.Col) - failedSamplesLock.Lock() - failedSamples = append(failedSamples, s) - failedSamplesLock.Unlock() - } - }(s) + // remove one second from the deadline to ensure we have enough time to process the results + samplingCtx, cancel := context.WithCancel(ctx) + if deadline, ok := ctx.Deadline(); ok { + samplingCtx, cancel = context.WithDeadline(ctx, deadline.Add(-time.Second)) } - wg.Wait() + defer cancel() - if errors.Is(ctx.Err(), context.Canceled) { - // Availability did not complete due to context cancellation, return context error instead of - // share.ErrNotAvailable - return ctx.Err() + smpls, errGetSamples := la.getter.GetSamples(samplingCtx, header, idxs) + if len(smpls) == 0 { + return share.ErrNotAvailable } - // store the result of the sampling session - bs := encodeSamples(failedSamples) + var failedSamples []shwap.SampleCoords + + for i, smpl := range smpls { + if smpl.IsEmpty() { + failedSamples = append(failedSamples, shwap.SampleCoords{Row: idxs[i].Row, Col: idxs[i].Col}) + } else { + samples.Available = append(samples.Available, shwap.SampleCoords{Row: idxs[i].Row, Col: idxs[i].Col}) + } + } + + samples.Remaining = failedSamples + + // Store the updated sampling result + updatedData, err := json.Marshal(samples) + if err != nil { + return err + } la.dsLk.Lock() - err = la.ds.Put(ctx, key, bs) + err = la.ds.Put(ctx, key, updatedData) la.dsLk.Unlock() if err != nil { - log.Errorw("Failed to store sampling result", "error", err) + return fmt.Errorf("store sampling result: %w", err) + } + + if errors.Is(errGetSamples, context.Canceled) { + // Availability did not complete due to context cancellation, return context error instead of + // share.ErrNotAvailable + return context.Canceled } // if any of the samples failed, return an error if len(failedSamples) > 0 { - log.Errorw("availability validation failed", - "root", dah.String(), - "failed_samples", failedSamples, - ) return share.ErrNotAvailable } + return nil } -func rootKey(root *share.Root) datastore.Key { +// Prune deletes samples and all sampling data corresponding to provided header from store. +// The operation will remove all data that ShareAvailable might have created +func (la *ShareAvailability) Prune(ctx context.Context, h *header.ExtendedHeader) error { + dah := h.DAH + if share.DataHash(dah.Hash()).IsEmptyEDS() { + return nil + } + + // Prevent multiple sampling and pruning sessions for the same header height + release, err := la.activeHeights.StartSession(ctx, h.Height()) + if err != nil { + return err + } + defer release() + + key := datastoreKeyForRoot(dah) + la.dsLk.RLock() + data, err := la.ds.Get(ctx, key) + la.dsLk.RUnlock() + if errors.Is(err, datastore.ErrNotFound) { + // nothing to prune + return nil + } + if err != nil { + return fmt.Errorf("get sampling result: %w", err) + } + + var result SamplingResult + err = json.Unmarshal(data, &result) + if err != nil { + return fmt.Errorf("unmarshal sampling result: %w", err) + } + + // delete stored samples + for _, sample := range result.Available { + idx := shwap.SampleCoords{Row: sample.Row, Col: sample.Col} + + blk, err := bitswap.NewEmptySampleBlock(h.Height(), idx, len(h.DAH.RowRoots)) + if err != nil { + return fmt.Errorf("marshal sample ID: %w", err) + } + err = la.bs.DeleteBlock(ctx, blk.CID()) + if err != nil { + if !errors.Is(err, ipld.ErrNodeNotFound) { + return fmt.Errorf("delete sample: %w", err) + } + log.Warnf("can't delete sample: %v, height: %v, missing in blockstore", sample, h.Height()) + } + } + + // delete the sampling result + la.dsLk.Lock() + err = la.ds.Delete(ctx, key) + la.dsLk.Unlock() + if err != nil { + return fmt.Errorf("delete sampling result: %w", err) + } + return nil +} + +func datastoreKeyForRoot(root *share.AxisRoots) datastore.Key { return datastore.NewKey(root.String()) } // Close flushes all queued writes to disk. func (la *ShareAvailability) Close(ctx context.Context) error { + la.dsLk.Lock() + defer la.dsLk.Unlock() return la.ds.Flush(ctx) } diff --git a/share/availability/light/availability_test.go b/share/availability/light/availability_test.go index 2f7b7a6cf9..df441c46a1 100644 --- a/share/availability/light/availability_test.go +++ b/share/availability/light/availability_test.go @@ -3,75 +3,139 @@ package light import ( "context" _ "embed" - "strconv" + "encoding/json" + "maps" + "slices" + "sync" + "sync/atomic" "testing" - + "time" + + "github.com/golang/mock/gomock" + "github.com/ipfs/boxo/bitswap/client" + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + ds_sync "github.com/ipfs/go-datastore/sync" + "github.com/libp2p/go-libp2p/core/host" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/nmt" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/header/headertest" "github.com/celestiaorg/celestia-node/share" - availability_test "github.com/celestiaorg/celestia-node/share/availability/test" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/sharetest" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/getters/mock" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/bitswap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" ) -func TestSharesAvailableCaches(t *testing.T) { +func TestSharesAvailableSuccess(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - getter, eh := GetterWithRandSquare(t, 16) - dah := eh.DAH - avail := TestAvailability(getter) - - // cache doesn't have dah yet - has, err := avail.ds.Has(ctx, rootKey(dah)) + square := edstest.RandEDS(t, 16) + roots, err := share.NewAxisRoots(square) + require.NoError(t, err) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + + getter := mock.NewMockGetter(gomock.NewController(t)) + getter.EXPECT(). + GetSamples(gomock.Any(), eh, gomock.Any()). + DoAndReturn( + func(_ context.Context, hdr *header.ExtendedHeader, indices []shwap.SampleCoords) ([]shwap.Sample, error) { + acc := eds.Rsmt2D{ExtendedDataSquare: square} + smpls := make([]shwap.Sample, len(indices)) + for i, idx := range indices { + smpl, err := acc.Sample(ctx, idx) + if err != nil { + return nil, err + } + + smpls[i] = smpl + } + return smpls, nil + }). + AnyTimes() + + ds := datastore.NewMapDatastore() + avail := NewShareAvailability(getter, ds, nil) + + // Ensure the datastore doesn't have the sampling result yet + has, err := avail.ds.Has(ctx, datastoreKeyForRoot(roots)) require.NoError(t, err) require.False(t, has) err = avail.SharesAvailable(ctx, eh) require.NoError(t, err) - // is now stored success result - result, err := avail.ds.Get(ctx, rootKey(dah)) + // Verify that the sampling result is stored with all samples marked as available + data, err := avail.ds.Get(ctx, datastoreKeyForRoot(roots)) require.NoError(t, err) - failed, err := decodeSamples(result) + + var result SamplingResult + err = json.Unmarshal(data, &result) require.NoError(t, err) - require.Empty(t, failed) + + require.Empty(t, result.Remaining) + require.Len(t, result.Available, int(avail.params.SampleAmount)) } -func TestSharesAvailableHitsCache(t *testing.T) { +func TestSharesAvailableSkipSampled(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - getter, _ := GetterWithRandSquare(t, 16) - avail := TestAvailability(getter) + // Create a getter that always returns ErrNotFound + getter := mock.NewMockGetter(gomock.NewController(t)) + getter.EXPECT(). + GetSamples(gomock.Any(), gomock.Any(), gomock.Any()). + Return(nil, shrex.ErrNotFound). + AnyTimes() - // create new dah, that is not available by getter - bServ := ipld.NewMemBlockservice() - dah := availability_test.RandFillBS(t, 16, bServ) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) + ds := datastore.NewMapDatastore() + avail := NewShareAvailability(getter, ds, nil) - // blockstore doesn't actually have the dah + // generate random header + roots := edstest.RandomAxisRoots(t, 16) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + + // store doesn't actually have the eds err := avail.SharesAvailable(ctx, eh) require.ErrorIs(t, err, share.ErrNotAvailable) - // put success result in cache - err = avail.ds.Put(ctx, rootKey(dah), []byte{}) + // Store a successful sampling result in the datastore + samplingResult := &SamplingResult{ + Available: make([]shwap.SampleCoords, avail.params.SampleAmount), + Remaining: []shwap.SampleCoords{}, + } + data, err := json.Marshal(samplingResult) + require.NoError(t, err) + err = avail.ds.Put(ctx, datastoreKeyForRoot(roots), data) require.NoError(t, err) - // should hit cache after putting + // SharesAvailable should now return nil since the success sampling result is stored err = avail.SharesAvailable(ctx, eh) require.NoError(t, err) } -func TestSharesAvailableEmptyRoot(t *testing.T) { +func TestSharesAvailableEmptyEDS(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - getter, _ := GetterWithRandSquare(t, 16) - avail := TestAvailability(getter) + getter := mock.NewMockGetter(gomock.NewController(t)) + ds := datastore.NewMapDatastore() + avail := NewShareAvailability(getter, ds, nil) - eh := headertest.RandExtendedHeaderWithRoot(t, share.EmptyRoot()) + // request for empty eds + eh := headertest.RandExtendedHeaderWithRoot(t, share.EmptyEDSRoots()) err := avail.SharesAvailable(ctx, eh) require.NoError(t, err) } @@ -80,178 +144,448 @@ func TestSharesAvailableFailed(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - getter, _ := GetterWithRandSquare(t, 16) - avail := TestAvailability(getter) + type test struct { + eh *header.ExtendedHeader + roots *share.AxisRoots + eds *rsmt2d.ExtendedDataSquare + } - // create new dah, that is not available by getter - bServ := ipld.NewMemBlockservice() - dah := availability_test.RandFillBS(t, 16, bServ) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) + tests := make([]test, 0) - // blockstore doesn't actually have the dah, so it should fail - err := avail.SharesAvailable(ctx, eh) - require.ErrorIs(t, err, share.ErrNotAvailable) + for i := 1; i <= 128; i *= 4 { + // Create new eds, that is not available by getter + eds := edstest.RandEDS(t, i) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) - // cache should have failed results now - result, err := avail.ds.Get(ctx, rootKey(dah)) - require.NoError(t, err) + tests = append(tests, test{eh: eh, roots: roots, eds: eds}) + } - failed, err := decodeSamples(result) - require.NoError(t, err) - require.Len(t, failed, int(avail.params.SampleAmount)) + for _, tt := range tests { + failGetter := mock.NewMockGetter(gomock.NewController(t)) + ds := datastore.NewMapDatastore() + avail := NewShareAvailability(failGetter, ds, nil) + + // Getter doesn't have the eds, so it should fail for all samples + mockSamples := min(int(avail.params.SampleAmount), 2*len(tt.eh.DAH.RowRoots)) + failGetter.EXPECT(). + GetSamples(gomock.Any(), gomock.Any(), gomock.Any()). + Return(make([]shwap.Sample, mockSamples), shrex.ErrNotFound). + AnyTimes() + + err := avail.SharesAvailable(ctx, tt.eh) + require.ErrorIs(t, err, share.ErrNotAvailable) + + // The datastore should now contain the sampling result with all samples in Remaining + data, err := avail.ds.Get(ctx, datastoreKeyForRoot(tt.roots)) + require.NoError(t, err) + + var failed SamplingResult + err = json.Unmarshal(data, &failed) + require.NoError(t, err) + + require.Empty(t, failed.Available) + if len(tt.roots.RowRoots)*len(tt.roots.RowRoots) < int(avail.params.SampleAmount) { + require.Len(t, failed.Remaining, len(tt.roots.RowRoots)*len(tt.roots.RowRoots)) + } else { + require.Len(t, failed.Remaining, int(avail.params.SampleAmount)) + } - // ensure that retry persists the failed samples selection - // create new getter with only the failed samples available, and add them to the onceGetter - onceGetter := newOnceGetter() - onceGetter.AddSamples(failed) + // Simulate a getter that now returns shares successfully + successfulGetter := newSuccessGetter() + avail.getter = successfulGetter - // replace getter with the new one - avail.getter = onceGetter + // should be able to retrieve all the failed samples now + err = avail.SharesAvailable(ctx, tt.eh) + require.NoError(t, err) - // should be able to retrieve all the failed samples now - err = avail.SharesAvailable(ctx, eh) - require.NoError(t, err) + // The sampling result should now have all samples in Available + data, err = avail.ds.Get(ctx, datastoreKeyForRoot(tt.roots)) + require.NoError(t, err) + + var result SamplingResult + err = json.Unmarshal(data, &result) + require.NoError(t, err) + + require.Empty(t, result.Remaining) + if len(tt.roots.RowRoots)*len(tt.roots.RowRoots) < int(avail.params.SampleAmount) { + require.Len(t, result.Available, len(tt.roots.RowRoots)*len(tt.roots.RowRoots)) + } else { + require.Len(t, result.Available, int(avail.params.SampleAmount)) + } - // onceGetter should have no more samples stored after the call - require.Empty(t, onceGetter.available) + // onceGetter should have no more samples stored after the call + require.ElementsMatch(t, failed.Remaining, successfulGetter.sampledList()) + successfulGetter.checkOnce(t) + } } -func TestShareAvailableOverMocknet_Light(t *testing.T) { +func TestParallelAvailability(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - net := availability_test.NewTestDAGNet(ctx, t) - _, root := RandNode(net, 16) - eh := headertest.RandExtendedHeader(t) - eh.DAH = root - nd := Node(net) - net.ConnectAll() + ds := datastore.NewMapDatastore() + // Simulate a getter that returns shares successfully + successfulGetter := newSuccessGetter() + avail := NewShareAvailability(successfulGetter, ds, nil) + + // create new eds, that is not available by getter + eds := edstest.RandEDS(t, 16) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + + var wg sync.WaitGroup + const iters = 100 + wg.Add(iters) + for i := 0; i < iters; i++ { + go func() { + defer wg.Done() + err := avail.SharesAvailable(ctx, eh) + require.NoError(t, err) + }() + } + wg.Wait() + require.Len(t, successfulGetter.sampledList(), int(avail.params.SampleAmount)) + successfulGetter.checkOnce(t) + + // Verify that the sampling result is stored with all samples marked as available + resultData, err := avail.ds.Get(ctx, datastoreKeyForRoot(roots)) + require.NoError(t, err) - err := nd.SharesAvailable(ctx, eh) + var samplingResult SamplingResult + err = json.Unmarshal(resultData, &samplingResult) require.NoError(t, err) + + require.Empty(t, samplingResult.Remaining) + require.Len(t, samplingResult.Available, int(avail.params.SampleAmount)) } -func TestGetShare(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() +type successGetter struct { + *sync.Mutex + sampled map[shwap.SampleCoords]int +} - n := 16 - getter, eh := GetterWithRandSquare(t, n) +func newSuccessGetter() successGetter { + return successGetter{ + Mutex: &sync.Mutex{}, + sampled: make(map[shwap.SampleCoords]int), + } +} - for i := range make([]bool, n) { - for j := range make([]bool, n) { - sh, err := getter.GetShare(ctx, eh, i, j) - require.NotNil(t, sh) - require.NoError(t, err) +func (g successGetter) sampledList() []shwap.SampleCoords { + g.Lock() + defer g.Unlock() + return slices.Collect(maps.Keys(g.sampled)) +} + +func (g successGetter) checkOnce(t *testing.T) { + g.Lock() + defer g.Unlock() + for s, count := range g.sampled { + if count > 1 { + t.Errorf("sample %v was called more than once", s) } } } -func TestService_GetSharesByNamespace(t *testing.T) { - tests := []struct { - squareSize int - expectedShareCount int - }{ - {squareSize: 4, expectedShareCount: 2}, - {squareSize: 16, expectedShareCount: 2}, - {squareSize: 128, expectedShareCount: 2}, +func (g successGetter) GetSamples(_ context.Context, _ *header.ExtendedHeader, + indices []shwap.SampleCoords, +) ([]shwap.Sample, error) { + g.Lock() + defer g.Unlock() + + smpls := make([]shwap.Sample, 0, len(indices)) + for _, idx := range indices { + s := shwap.SampleCoords{Row: idx.Row, Col: idx.Col} + g.sampled[s]++ + smpls = append(smpls, shwap.Sample{Proof: &nmt.Proof{}}) } + return smpls, nil +} - for _, tt := range tests { - t.Run("size: "+strconv.Itoa(tt.squareSize), func(t *testing.T) { - getter, bServ := EmptyGetter() - totalShares := tt.squareSize * tt.squareSize - randShares := sharetest.RandShares(t, totalShares) - idx1 := (totalShares - 1) / 2 - idx2 := totalShares / 2 - if tt.expectedShareCount > 1 { - // make it so that two rows have the same namespace - copy(share.GetNamespace(randShares[idx2]), share.GetNamespace(randShares[idx1])) - } - root := availability_test.FillBS(t, bServ, randShares) - eh := headertest.RandExtendedHeader(t) - eh.DAH = root - randNamespace := share.GetNamespace(randShares[idx1]) - - shares, err := getter.GetSharesByNamespace(context.Background(), eh, randNamespace) - require.NoError(t, err) - require.NoError(t, shares.Verify(root, randNamespace)) - flattened := shares.Flatten() - require.Len(t, flattened, tt.expectedShareCount) - for _, value := range flattened { - require.Equal(t, randNamespace, share.GetNamespace(value)) - } - if tt.expectedShareCount > 1 { - // idx1 is always smaller than idx2 - require.Equal(t, randShares[idx1], flattened[0]) - require.Equal(t, randShares[idx2], flattened[1]) - } - }) - t.Run("last two rows of a 4x4 square that have the same namespace have valid NMT proofs", func(t *testing.T) { - squareSize := 4 - totalShares := squareSize * squareSize - getter, bServ := EmptyGetter() - randShares := sharetest.RandShares(t, totalShares) - lastNID := share.GetNamespace(randShares[totalShares-1]) - for i := totalShares / 2; i < totalShares; i++ { - copy(share.GetNamespace(randShares[i]), lastNID) - } - root := availability_test.FillBS(t, bServ, randShares) - eh := headertest.RandExtendedHeader(t) - eh.DAH = root - - shares, err := getter.GetSharesByNamespace(context.Background(), eh, lastNID) - require.NoError(t, err) - require.NoError(t, shares.Verify(root, lastNID)) - }) - } +func (g successGetter) GetRow(_ context.Context, _ *header.ExtendedHeader, _ int) (shwap.Row, error) { + panic("not implemented") } -func TestGetShares(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() +func (g successGetter) GetEDS(_ context.Context, _ *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { + panic("not implemented") +} + +func (g successGetter) GetNamespaceData( + _ context.Context, + _ *header.ExtendedHeader, + _ libshare.Namespace, +) (shwap.NamespaceData, error) { + panic("not implemented") +} + +func TestPruneAll(t *testing.T) { + const size = 8 + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + t.Cleanup(cancel) + + eds, h := randEdsAndHeader(t, size) + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + clientBs := blockstore.NewBlockstore(ds) + + ex := newExchangeOverEDS(ctx, t, eds) + getter := bitswap.NewGetter(ex, clientBs, 0) + getter.Start() + defer getter.Stop() + + // Create a new ShareAvailability instance and sample the shares + sampleAmount := uint(20) + avail := NewShareAvailability(getter, ds, clientBs, WithSampleAmount(sampleAmount)) + err := avail.SharesAvailable(ctx, h) + require.NoError(t, err) + // close ShareAvailability to force flush of batched writes + avail.Close(ctx) + + preDeleteCount := countKeys(ctx, t, clientBs) + require.EqualValues(t, sampleAmount, preDeleteCount) + + // prune the samples + err = avail.Prune(ctx, h) + require.NoError(t, err) + + // Check if samples are deleted + postDeleteCount := countKeys(ctx, t, clientBs) + require.Zero(t, postDeleteCount) + + // Check if sampling result is deleted + exist, err := avail.ds.Has(ctx, datastoreKeyForRoot(h.DAH)) + require.NoError(t, err) + require.False(t, exist) +} + +func TestPrunePartialFailed(t *testing.T) { + const size = 8 + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + t.Cleanup(cancel) + + eds, h := randEdsAndHeader(t, size) + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + clientBs := blockstore.NewBlockstore(ds) + + ex := newHalfSessionExchange(newExchangeOverEDS(ctx, t, eds)) + getter := bitswap.NewGetter(ex, clientBs, 0) + getter.Start() + defer getter.Stop() + + // Create a new ShareAvailability instance and sample the shares + sampleAmount := uint(20) + avail := NewShareAvailability(getter, ds, clientBs, WithSampleAmount(sampleAmount)) + err := avail.SharesAvailable(ctx, h) + require.Error(t, err) + // close ShareAvailability to force flush of batched writes + avail.Close(ctx) + + preDeleteCount := countKeys(ctx, t, clientBs) + // Only half of the samples should be stored + require.EqualValues(t, sampleAmount/2, preDeleteCount) + + // prune the samples + err = avail.Prune(ctx, h) + require.NoError(t, err) - n := 16 - getter, eh := GetterWithRandSquare(t, n) + // Check if samples are deleted + postDeleteCount := countKeys(ctx, t, clientBs) + require.Zero(t, postDeleteCount) - eds, err := getter.GetEDS(ctx, eh) + // Check if sampling result is deleted + exist, err := avail.ds.Has(ctx, datastoreKeyForRoot(h.DAH)) require.NoError(t, err) - gotDAH, err := share.NewRoot(eds) + require.False(t, exist) +} + +func TestPruneWithCancelledContext(t *testing.T) { + const size = 8 + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + t.Cleanup(cancel) + + eds, h := randEdsAndHeader(t, size) + ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) + clientBs := blockstore.NewBlockstore(ds) + + ex := newTimeoutExchange(newExchangeOverEDS(ctx, t, eds)) + getter := bitswap.NewGetter(ex, clientBs, 0) + getter.Start() + defer getter.Stop() + + // Create a new ShareAvailability instance and sample the shares + sampleAmount := uint(20) + avail := NewShareAvailability(getter, ds, clientBs, WithSampleAmount(sampleAmount)) + + ctx2, cancel2 := context.WithTimeout(ctx, 1500*time.Millisecond) + defer cancel2() + go func() { + // cancel context a bit later. + time.Sleep(100 * time.Millisecond) + cancel2() + }() + + err := avail.SharesAvailable(ctx2, h) + require.Error(t, err, context.Canceled) + // close ShareAvailability to force flush of batched writes + avail.Close(ctx) + + preDeleteCount := countKeys(ctx, t, clientBs) + require.EqualValues(t, sampleAmount, preDeleteCount) + + // prune the samples + err = avail.Prune(ctx, h) require.NoError(t, err) - require.True(t, eh.DAH.Equals(gotDAH)) + // Check if samples are deleted + postDeleteCount := countKeys(ctx, t, clientBs) + require.Zero(t, postDeleteCount) + + // Check if sampling result is deleted + exist, err := avail.ds.Has(ctx, datastoreKeyForRoot(h.DAH)) + require.NoError(t, err) + require.False(t, exist) +} + +type halfSessionExchange struct { + exchange.SessionExchange + attempt atomic.Int32 +} + +func newHalfSessionExchange(ex exchange.SessionExchange) *halfSessionExchange { + return &halfSessionExchange{SessionExchange: ex} +} + +func (hse *halfSessionExchange) NewSession(context.Context) exchange.Fetcher { + return hse +} + +func (hse *halfSessionExchange) GetBlocks(ctx context.Context, cids []cid.Cid) (<-chan blocks.Block, error) { + out := make(chan blocks.Block, len(cids)) + defer close(out) + + for _, cid := range cids { + if hse.attempt.Add(1)%2 == 0 { + continue + } + + blk, err := hse.SessionExchange.GetBlock(ctx, cid) + if err != nil { + return nil, err + } + + out <- blk + } + + return out, nil +} + +type timeoutExchange struct { + exchange.SessionExchange +} + +func newTimeoutExchange(ex exchange.SessionExchange) *timeoutExchange { + return &timeoutExchange{SessionExchange: ex} +} + +func (hse *timeoutExchange) NewSession(context.Context) exchange.Fetcher { + return hse } -func TestService_GetSharesByNamespaceNotFound(t *testing.T) { - getter, eh := GetterWithRandSquare(t, 1) - eh.DAH.RowRoots = nil +func (hse *timeoutExchange) GetBlocks(ctx context.Context, cids []cid.Cid) (<-chan blocks.Block, error) { + out := make(chan blocks.Block, len(cids)) + defer close(out) - emptyShares, err := getter.GetSharesByNamespace(context.Background(), eh, sharetest.RandV0Namespace()) + for _, cid := range cids { + blk, err := hse.SessionExchange.GetBlock(ctx, cid) + if err != nil { + return nil, err + } + + out <- blk + } + + // sleep guarantees that we context will be canceled in a test. + time.Sleep(200 * time.Millisecond) + + return out, nil +} + +func randEdsAndHeader(t *testing.T, size int) (*rsmt2d.ExtendedDataSquare, *header.ExtendedHeader) { + height := uint64(42) + eds := edstest.RandEDS(t, size) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - require.Empty(t, emptyShares.Flatten()) + + h := &header.ExtendedHeader{ + RawHeader: header.RawHeader{ + Height: int64(height), + Time: time.Now(), + }, + DAH: roots, + } + return eds, h } -func BenchmarkService_GetSharesByNamespace(b *testing.B) { - tests := []struct { - amountShares int - }{ - {amountShares: 4}, - {amountShares: 16}, - {amountShares: 128}, +func countKeys(ctx context.Context, t *testing.T, bs blockstore.Blockstore) int { + keys, err := bs.AllKeysChan(ctx) + require.NoError(t, err) + var count int + for range keys { + count++ } + return count +} - for _, tt := range tests { - b.Run(strconv.Itoa(tt.amountShares), func(b *testing.B) { - t := &testing.T{} - getter, eh := GetterWithRandSquare(t, tt.amountShares) - root := eh.DAH - randNamespace := root.RowRoots[(len(root.RowRoots)-1)/2][:share.NamespaceSize] - root.RowRoots[(len(root.RowRoots) / 2)] = root.RowRoots[(len(root.RowRoots)-1)/2] - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := getter.GetSharesByNamespace(context.Background(), eh, randNamespace) - require.NoError(t, err) - } - }) +func newExchangeOverEDS(ctx context.Context, t *testing.T, rsmt2d *rsmt2d.ExtendedDataSquare) exchange.SessionExchange { + bstore := &bitswap.Blockstore{ + Getter: testAccessorGetter{ + AccessorStreamer: &eds.Rsmt2D{ExtendedDataSquare: rsmt2d}, + }, } + return newExchange(ctx, t, bstore) +} + +func newExchange(ctx context.Context, t *testing.T, bstore blockstore.Blockstore) exchange.SessionExchange { + net, err := mocknet.FullMeshLinked(3) + require.NoError(t, err) + + newServer(ctx, net.Hosts()[0], bstore) + newServer(ctx, net.Hosts()[1], bstore) + + client := newClient(ctx, net.Hosts()[2], bstore) + + err = net.ConnectAllButSelf() + require.NoError(t, err) + return client +} + +func newServer(ctx context.Context, host host.Host, store blockstore.Blockstore) { + net := bitswap.NewNetwork(host, "test") + server := bitswap.NewServer( + ctx, + net, + store, + ) + net.Start(server) +} + +func newClient(ctx context.Context, host host.Host, store blockstore.Blockstore) *client.Client { + net := bitswap.NewNetwork(host, "test") + client := bitswap.NewClient(ctx, net, store) + net.Start(client) + return client +} + +type testAccessorGetter struct { + eds.AccessorStreamer +} + +func (t testAccessorGetter) GetByHeight(context.Context, uint64) (eds.AccessorStreamer, error) { + return t.AccessorStreamer, nil +} + +func (t testAccessorGetter) HasByHeight(context.Context, uint64) (bool, error) { + return true, nil } diff --git a/share/availability/light/options.go b/share/availability/light/options.go index 80fd27acfd..3ed8038f51 100644 --- a/share/availability/light/options.go +++ b/share/availability/light/options.go @@ -4,7 +4,7 @@ import ( "fmt" ) -// SampleAmount specifies the minimum required amount of samples a light node must perform +// DefaultSampleAmount specifies the minimum required amount of samples a light node must perform // before declaring that a block is available var ( DefaultSampleAmount uint = 16 @@ -21,8 +21,8 @@ type Option func(*Parameters) // DefaultParameters returns the default Parameters' configuration values // for the light availability implementation -func DefaultParameters() Parameters { - return Parameters{ +func DefaultParameters() *Parameters { + return &Parameters{ SampleAmount: DefaultSampleAmount, } } diff --git a/share/availability/light/sample.go b/share/availability/light/sample.go index c8061cdb1e..fc0b41d08a 100644 --- a/share/availability/light/sample.go +++ b/share/availability/light/sample.go @@ -1,104 +1,57 @@ -// TODO(@Wondertan): Instead of doing sampling over the coordinates do a random walk over NMT trees. package light import ( crand "crypto/rand" - "encoding/binary" - "errors" + "maps" "math/big" + "slices" + + "github.com/celestiaorg/celestia-node/share/shwap" ) -// Sample is a point in 2D space over square. -type Sample struct { - Row, Col uint16 +// SamplingResult holds the available and remaining samples. +type SamplingResult struct { + Available []shwap.SampleCoords `json:"available"` + Remaining []shwap.SampleCoords `json:"remaining"` } -// SampleSquare randomly picks *num* unique points from the given *width* square -// and returns them as samples. -func SampleSquare(squareWidth, num int) ([]Sample, error) { - ss := newSquareSampler(squareWidth, num) - err := ss.generateSample(num) - if err != nil { - return nil, err +// NewSamplingResult creates a new SamplingResult with randomly selected samples. +func NewSamplingResult(squareSize, sampleCount int) *SamplingResult { + total := squareSize * squareSize + if sampleCount > total { + sampleCount = total } - return ss.samples(), nil -} - -type squareSampler struct { - squareWidth int - smpls map[Sample]struct{} -} -func newSquareSampler(squareWidth, expectedSamples int) *squareSampler { - return &squareSampler{ - squareWidth: squareWidth, - smpls: make(map[Sample]struct{}, expectedSamples), + samples := selectRandomSamples(squareSize, sampleCount) + return &SamplingResult{ + Remaining: samples, } } -// generateSample randomly picks unique point on a 2D spaces. -func (ss *squareSampler) generateSample(num int) error { - if num > ss.squareWidth*ss.squareWidth { - num = ss.squareWidth +// selectRandomSamples randomly picks unique coordinates from a square of given size. +func selectRandomSamples(squareSize, sampleCount int) []shwap.SampleCoords { + total := squareSize * squareSize + if sampleCount > total { + sampleCount = total } - done := 0 - for done < num { - s := Sample{ - Row: randInt(ss.squareWidth), - Col: randInt(ss.squareWidth), + samples := make(map[shwap.SampleCoords]struct{}, sampleCount) + for len(samples) < sampleCount { + s := shwap.SampleCoords{ + Row: randInt(squareSize), + Col: randInt(squareSize), } - - if _, ok := ss.smpls[s]; ok { - continue - } - - done++ - ss.smpls[s] = struct{}{} - } - - return nil -} - -func (ss *squareSampler) samples() []Sample { - samples := make([]Sample, 0, len(ss.smpls)) - for s := range ss.smpls { - samples = append(samples, s) + samples[s] = struct{}{} } - return samples + return slices.Collect(maps.Keys(samples)) } -func randInt(max int) uint16 { - n, err := crand.Int(crand.Reader, big.NewInt(int64(max))) +func randInt(m int) int { + n, err := crand.Int(crand.Reader, big.NewInt(int64(m))) if err != nil { panic(err) // won't panic as rand.Reader is endless } - return uint16(n.Uint64()) -} - -// encodeSamples encodes a slice of samples into a byte slice using little endian encoding. -func encodeSamples(samples []Sample) []byte { - bs := make([]byte, 0, len(samples)*4) - for _, s := range samples { - bs = binary.LittleEndian.AppendUint16(bs, s.Row) - bs = binary.LittleEndian.AppendUint16(bs, s.Col) - } - return bs -} - -// decodeSamples decodes a byte slice into a slice of samples. -func decodeSamples(bs []byte) ([]Sample, error) { - if len(bs)%4 != 0 { - return nil, errors.New("invalid byte slice length") - } - - samples := make([]Sample, 0, len(bs)/4) - for i := 0; i < len(bs); i += 4 { - samples = append(samples, Sample{ - Row: binary.LittleEndian.Uint16(bs[i : i+2]), - Col: binary.LittleEndian.Uint16(bs[i+2 : i+4]), - }) - } - return samples, nil + // n.Uint64() is safe as max is int + return int(n.Uint64()) } diff --git a/share/availability/light/sample_test.go b/share/availability/light/sample_test.go index 8d7656e688..2bdbb223b6 100644 --- a/share/availability/light/sample_test.go +++ b/share/availability/light/sample_test.go @@ -16,13 +16,12 @@ func TestSampleSquare(t *testing.T) { } for _, tt := range tests { - ss, err := SampleSquare(tt.width, tt.samples) - assert.NoError(t, err) + ss := selectRandomSamples(tt.width, tt.samples) assert.Len(t, ss, tt.samples) // check points are within width for _, s := range ss { - assert.Less(t, int(s.Row), tt.width) - assert.Less(t, int(s.Col), tt.width) + assert.Less(t, s.Row, tt.width) + assert.Less(t, s.Col, tt.width) } // checks samples are not equal for i, s1 := range ss { diff --git a/share/availability/light/testing.go b/share/availability/light/testing.go index b6251b4fbd..239bfa1ba3 100644 --- a/share/availability/light/testing.go +++ b/share/availability/light/testing.go @@ -1,107 +1,108 @@ package light -import ( - "context" - "sync" - "testing" - - "github.com/ipfs/boxo/blockservice" - "github.com/ipfs/go-datastore" - - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/header/headertest" - "github.com/celestiaorg/celestia-node/share" - availability_test "github.com/celestiaorg/celestia-node/share/availability/test" - "github.com/celestiaorg/celestia-node/share/getters" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -// GetterWithRandSquare provides a share.Getter filled with 'n' NMT trees of 'n' random shares, -// essentially storing a whole square. -func GetterWithRandSquare(t *testing.T, n int) (share.Getter, *header.ExtendedHeader) { - bServ := ipld.NewMemBlockservice() - getter := getters.NewIPLDGetter(bServ) - root := availability_test.RandFillBS(t, n, bServ) - eh := headertest.RandExtendedHeader(t) - eh.DAH = root - - return getter, eh -} - -// EmptyGetter provides an unfilled share.Getter with corresponding blockservice.BlockService than -// can be filled by the test. -func EmptyGetter() (share.Getter, blockservice.BlockService) { - bServ := ipld.NewMemBlockservice() - getter := getters.NewIPLDGetter(bServ) - return getter, bServ -} - -// RandNode creates a Light Node filled with a random block of the given size. -func RandNode(dn *availability_test.TestDagNet, squareSize int) (*availability_test.TestNode, *share.Root) { - nd := Node(dn) - return nd, availability_test.RandFillBS(dn.T, squareSize, nd.BlockService) -} - -// Node creates a new empty Light Node. -func Node(dn *availability_test.TestDagNet) *availability_test.TestNode { - nd := dn.NewTestNode() - nd.Getter = getters.NewIPLDGetter(nd.BlockService) - nd.Availability = TestAvailability(nd.Getter) - return nd -} - -func TestAvailability(getter share.Getter) *ShareAvailability { - ds := datastore.NewMapDatastore() - return NewShareAvailability(getter, ds) -} - -func SubNetNode(sn *availability_test.SubNet) *availability_test.TestNode { - nd := Node(sn.TestDagNet) - sn.AddNode(nd) - return nd -} - -type onceGetter struct { - *sync.Mutex - available map[Sample]struct{} -} - -func newOnceGetter() onceGetter { - return onceGetter{ - Mutex: &sync.Mutex{}, - available: make(map[Sample]struct{}), - } -} - -func (m onceGetter) AddSamples(samples []Sample) { - m.Lock() - defer m.Unlock() - for _, s := range samples { - m.available[s] = struct{}{} - } -} - -func (m onceGetter) GetShare(_ context.Context, _ *header.ExtendedHeader, row, col int) (share.Share, error) { - m.Lock() - defer m.Unlock() - s := Sample{Row: uint16(row), Col: uint16(col)} - if _, ok := m.available[s]; ok { - delete(m.available, s) - return share.Share{}, nil - } - return share.Share{}, share.ErrNotAvailable -} - -func (m onceGetter) GetEDS(_ context.Context, _ *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { - panic("not implemented") -} - -func (m onceGetter) GetSharesByNamespace( - _ context.Context, - _ *header.ExtendedHeader, - _ share.Namespace, -) (share.NamespacedShares, error) { - panic("not implemented") -} +// +// import ( +// "context" +// "sync" +// "testing" +// +// "github.com/ipfs/boxo/blockservice" +// "github.com/ipfs/go-datastore" +// +// "github.com/celestiaorg/rsmt2d" +// +// "github.com/celestiaorg/celestia-node/header" +// "github.com/celestiaorg/celestia-node/header/headertest" +// "github.com/celestiaorg/celestia-node/share" +// availability_test "github.com/celestiaorg/celestia-node/share/availability/test" +// "github.com/celestiaorg/celestia-node/share/getters" +// "github.com/celestiaorg/celestia-node/share/ipld" +//) +// +//// GetterWithRandSquare provides a share.Getter filled with 'n' NMT trees of 'n' random shares, +//// essentially storing a whole square. +// func GetterWithRandSquare(t *testing.T, n int) (share.Getter, *header.ExtendedHeader) { +// bServ := ipld.NewMemBlockservice() +// getter := getters.NewIPLDGetter(bServ) +// root := availability_test.RandFillBS(t, n, bServ) +// eh := headertest.RandExtendedHeader(t) +// eh.DAH = root +// +// return getter, eh +//} +// +//// EmptyGetter provides an unfilled share.Getter with corresponding blockservice.BlockService than +//// can be filled by the test. +// func EmptyGetter() (share.Getter, blockservice.BlockService) { +// bServ := ipld.NewMemBlockservice() +// getter := getters.NewIPLDGetter(bServ) +// return getter, bServ +//} +// +//// RandNode creates a Light Node filled with a random block of the given size. +// func RandNode(dn *availability_test.TestDagNet, squareSize int) (*availability_test.TestNode, *share.AxisRoots) { +// nd := Node(dn) +// return nd, availability_test.RandFillBS(dn.T, squareSize, nd.BlockService) +//} +// +//// Node creates a new empty Light Node. +// func Node(dn *availability_test.TestDagNet) *availability_test.TestNode { +// nd := dn.NewTestNode() +// nd.Getter = getters.NewIPLDGetter(nd.BlockService) +// nd.Availability = TestAvailability(nd.Getter) +// return nd +//} +// +// func TestAvailability(getter share.Getter) *ShareAvailability { +// ds := datastore.NewMapDatastore() +// return NewShareAvailability(getter, ds) +//} +// +// func SubNetNode(sn *availability_test.SubNet) *availability_test.TestNode { +// nd := Node(sn.TestDagNet) +// sn.AddNode(nd) +// return nd +//} +// +// type onceGetter struct { +// *sync.Mutex +// available map[Sample]struct{} +//} +// +// func newOnceGetter() onceGetter { +// return onceGetter{ +// Mutex: &sync.Mutex{}, +// available: make(map[Sample]struct{}), +// } +//} +// +// func (m onceGetter) AddSamples(samples []Sample) { +// m.Lock() +// defer m.Unlock() +// for _, s := range samples { +// m.available[s] = struct{}{} +// } +//} +// +// func (m onceGetter) GetShare(_ context.Context, _ *header.ExtendedHeader, row, col int) (libshare.Share, error) { +// m.Lock() +// defer m.Unlock() +// s := Sample{Row: uint16(row), Col: uint16(col)} +// if _, ok := m.available[s]; ok { +// delete(m.available, s) +// return libshare.Share{}, nil +// } +// return libshare.Share{}, share.ErrNotAvailable +//} +// +// func (m onceGetter) GetEDS(_ context.Context, _ *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { +// panic("not implemented") +//} +// +// func (m onceGetter) GetSharesByNamespace( +// _ context.Context, +// _ *header.ExtendedHeader, +// _ libshare.Namespace, +// ) (libshare.NamespacedShares, error) { +// panic("not implemented") +// } diff --git a/share/availability/test/corrupt_data.go b/share/availability/test/corrupt_data.go deleted file mode 100644 index 1ff553f8b3..0000000000 --- a/share/availability/test/corrupt_data.go +++ /dev/null @@ -1,130 +0,0 @@ -package availability_test - -import ( - "context" - "crypto/rand" - "fmt" - mrand "math/rand" - "testing" - - "github.com/ipfs/boxo/blockstore" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - ds "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" -) - -var _ blockstore.Blockstore = (*FraudulentBlockstore)(nil) - -// CorruptBlock is a block where the cid doesn't match the data. It fulfills the blocks.Block -// interface. -type CorruptBlock struct { - cid cid.Cid - data []byte -} - -func (b *CorruptBlock) RawData() []byte { - return b.data -} - -func (b *CorruptBlock) Cid() cid.Cid { - return b.cid -} - -func (b *CorruptBlock) String() string { - return fmt.Sprintf("[Block %s]", b.Cid()) -} - -func (b *CorruptBlock) Loggable() map[string]interface{} { - return map[string]interface{}{ - "block": b.Cid().String(), - } -} - -func NewCorruptBlock(data []byte, fakeCID cid.Cid) *CorruptBlock { - return &CorruptBlock{ - fakeCID, - data, - } -} - -// FraudulentBlockstore is a mock blockstore.Blockstore that saves both corrupted and original data -// for every block it receives. If FraudulentBlockstore.Attacking is true, it will serve the -// corrupted data on requests. -type FraudulentBlockstore struct { - ds.Datastore - Attacking bool -} - -func (fb FraudulentBlockstore) Has(context.Context, cid.Cid) (bool, error) { - return false, nil -} - -func (fb FraudulentBlockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) { - key := cid.String() - if fb.Attacking { - key = "corrupt_get" + key - } - - data, err := fb.Datastore.Get(ctx, ds.NewKey(key)) - if err != nil { - return nil, err - } - return NewCorruptBlock(data, cid), nil -} - -func (fb FraudulentBlockstore) GetSize(ctx context.Context, cid cid.Cid) (int, error) { - key := cid.String() - if fb.Attacking { - key = "corrupt_size" + key - } - - return fb.Datastore.GetSize(ctx, ds.NewKey(key)) -} - -func (fb FraudulentBlockstore) Put(ctx context.Context, block blocks.Block) error { - err := fb.Datastore.Put(ctx, ds.NewKey(block.Cid().String()), block.RawData()) - if err != nil { - return err - } - - // create data that doesn't match the CID with arbitrary lengths between 1 and - // len(block.RawData())*2 - corrupted := make([]byte, 1+mrand.Int()%(len(block.RawData())*2-1)) //nolint:gosec - _, _ = rand.Read(corrupted) - return fb.Datastore.Put(ctx, ds.NewKey("corrupt"+block.Cid().String()), corrupted) -} - -func (fb FraudulentBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { - for _, b := range blocks { - err := fb.Put(ctx, b) - if err != nil { - return err - } - } - return nil -} - -func (fb FraudulentBlockstore) DeleteBlock(context.Context, cid.Cid) error { - panic("implement me") -} - -func (fb FraudulentBlockstore) AllKeysChan(context.Context) (<-chan cid.Cid, error) { - panic("implement me") -} - -func (fb FraudulentBlockstore) HashOnRead(bool) { - panic("implement me") -} - -// MockNode creates a TestNode that uses a FraudulentBlockstore to simulate serving corrupted data. -func MockNode(t *testing.T, net *TestDagNet) (*TestNode, *FraudulentBlockstore) { - t.Helper() - dstore := dssync.MutexWrap(ds.NewMapDatastore()) - mockBS := &FraudulentBlockstore{ - Datastore: dstore, - Attacking: false, - } - provider := net.NewTestNodeWithBlockstore(dstore, mockBS) - return provider, mockBS -} diff --git a/share/availability/test/testing.go b/share/availability/test/testing.go index 64e8d23bb7..e9bee74357 100644 --- a/share/availability/test/testing.go +++ b/share/availability/test/testing.go @@ -17,29 +17,32 @@ import ( mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/sharetest" + "github.com/celestiaorg/celestia-node/share/shwap" ) // RandFillBS fills the given BlockService with a random block of a given size. -func RandFillBS(t *testing.T, n int, bServ blockservice.BlockService) *share.Root { - shares := sharetest.RandShares(t, n*n) +func RandFillBS(t *testing.T, n int, bServ blockservice.BlockService) *share.AxisRoots { + shares, err := libshare.RandShares(n * n) + require.NoError(t, err) return FillBS(t, bServ, shares) } // FillBS fills the given BlockService with the given shares. -func FillBS(t *testing.T, bServ blockservice.BlockService, shares []share.Share) *share.Root { +func FillBS(t *testing.T, bServ blockservice.BlockService, shares []libshare.Share) *share.AxisRoots { eds, err := ipld.AddShares(context.TODO(), shares, bServ) require.NoError(t, err) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - return dah + return roots } type TestNode struct { net *TestDagNet - share.Getter + shwap.Getter share.Availability blockservice.BlockService host.Host diff --git a/share/availability/window.go b/share/availability/window.go new file mode 100644 index 0000000000..cdf60c106b --- /dev/null +++ b/share/availability/window.go @@ -0,0 +1,49 @@ +package availability + +import ( + "errors" + "os" + "time" +) + +const ( + RequestWindow = 30 * 24 * time.Hour + StorageWindow = RequestWindow + time.Hour +) + +var ErrOutsideSamplingWindow = errors.New("timestamp outside sampling window") + +// IsWithinWindow checks whether the given timestamp is within the +// given AvailabilityWindow. If the window is disabled (0), it returns true for +// every timestamp. +func IsWithinWindow(t time.Time, window time.Duration) bool { + if windowOverride { + window = windowOverrideDur + } + if window == time.Duration(0) { + return true + } + return time.Since(t) <= window +} + +// windowOverride is a flag that overrides the availability window. This flag is intended for +// testing purposes only. +var ( + windowOverride bool + windowOverrideDur time.Duration +) + +func init() { + durationRaw, ok := os.LookupEnv("CELESTIA_OVERRIDE_AVAILABILITY_WINDOW") + if !ok { + return + } + + duration, err := time.ParseDuration(durationRaw) + if err != nil { + panic("failed to parse CELESTIA_OVERRIDE_AVAILABILITY_WINDOW: " + err.Error()) + } + + windowOverride = true + windowOverrideDur = duration +} diff --git a/share/availability/window_test.go b/share/availability/window_test.go new file mode 100644 index 0000000000..af1f95c4d6 --- /dev/null +++ b/share/availability/window_test.go @@ -0,0 +1,15 @@ +package availability + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// TestConsts exists to ensure that any changes to the sampling windows +// are deliberate. +func TestConsts(t *testing.T) { + assert.Equal(t, RequestWindow, 30*24*time.Hour) + assert.Equal(t, StorageWindow, (30*24*time.Hour)+time.Hour) +} diff --git a/share/doc.go b/share/doc.go index 97229932a7..6c6426472f 100644 --- a/share/doc.go +++ b/share/doc.go @@ -5,7 +5,7 @@ block data. Though this package contains several useful methods for getting specific shares and/or sampling them at random, a particularly useful method is GetSharesByNamespace which retrieves all shares of block data of the given Namespace from the block associated with the given -DataAvailabilityHeader (DAH, but referred to as Root within this package). +DataAvailabilityHeader (DAH, but referred to as AxisRoots within this package). This package also contains declaration of the Availability interface. Implementations of the interface (light, full) are located in the availability sub-folder. diff --git a/share/eds/accessor.go b/share/eds/accessor.go new file mode 100644 index 0000000000..eb3fea6e41 --- /dev/null +++ b/share/eds/accessor.go @@ -0,0 +1,58 @@ +package eds + +import ( + "context" + "io" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +// EmptyAccessor is an accessor of an empty EDS block. +var EmptyAccessor = &Rsmt2D{ExtendedDataSquare: share.EmptyEDS()} + +// Accessor is an interface for accessing extended data square data. +type Accessor interface { + // Size returns square size of the Accessor. + Size(ctx context.Context) int + // DataHash returns data hash of the Accessor. + DataHash(ctx context.Context) (share.DataHash, error) + // AxisRoots returns share.AxisRoots (DataAvailabilityHeader) of the Accessor. + AxisRoots(ctx context.Context) (*share.AxisRoots, error) + // Sample returns share and corresponding proof for row and column indices. Implementation can + // choose which axis to use for proof. Chosen axis for proof should be indicated in the returned + // Sample. + Sample(ctx context.Context, idx shwap.SampleCoords) (shwap.Sample, error) + // AxisHalf returns half of shares axis of the given type and index. Side is determined by + // implementation. Implementations should indicate the side in the returned AxisHalf. + AxisHalf(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) (AxisHalf, error) + // RowNamespaceData returns data for the given namespace and row index. + RowNamespaceData(ctx context.Context, namespace libshare.Namespace, rowIdx int) (shwap.RowNamespaceData, error) + // Shares returns data (ODS) shares extracted from the Accessor. + Shares(ctx context.Context) ([]libshare.Share, error) +} + +// AccessorStreamer is an interface that groups Accessor and Streamer interfaces. +type AccessorStreamer interface { + Accessor + Streamer +} + +type Streamer interface { + // Reader returns binary reader for the shares. It should read the shares from the + // ODS part of the square row by row. + Reader() (io.Reader, error) + io.Closer +} + +type accessorStreamer struct { + Accessor + Streamer +} + +func AccessorAndStreamer(a Accessor, s Streamer) AccessorStreamer { + return &accessorStreamer{a, s} +} diff --git a/share/eds/adapters.go b/share/eds/adapters.go deleted file mode 100644 index 8bf2340d91..0000000000 --- a/share/eds/adapters.go +++ /dev/null @@ -1,66 +0,0 @@ -package eds - -import ( - "context" - "sync" - - "github.com/filecoin-project/dagstore" - "github.com/ipfs/boxo/blockservice" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" -) - -var _ blockservice.BlockGetter = (*BlockGetter)(nil) - -// NewBlockGetter creates new blockservice.BlockGetter adapter from dagstore.ReadBlockstore -func NewBlockGetter(store dagstore.ReadBlockstore) *BlockGetter { - return &BlockGetter{store: store} -} - -// BlockGetter is an adapter for dagstore.ReadBlockstore to implement blockservice.BlockGetter -// interface. -type BlockGetter struct { - store dagstore.ReadBlockstore -} - -// GetBlock gets the requested block by the given CID. -func (bg *BlockGetter) GetBlock(ctx context.Context, cid cid.Cid) (blocks.Block, error) { - return bg.store.Get(ctx, cid) -} - -// GetBlocks does a batch request for the given cids, returning blocks as -// they are found, in no particular order. -// -// It implements blockservice.BlockGetter interface, that requires: -// It may not be able to find all requested blocks (or the context may -// be canceled). In that case, it will close the channel early. It is up -// to the consumer to detect this situation and keep track which blocks -// it has received and which it hasn't. -func (bg *BlockGetter) GetBlocks(ctx context.Context, cids []cid.Cid) <-chan blocks.Block { - bCh := make(chan blocks.Block) - - go func() { - var wg sync.WaitGroup - wg.Add(len(cids)) - for _, c := range cids { - go func(cid cid.Cid) { - defer wg.Done() - block, err := bg.store.Get(ctx, cid) - if err != nil { - log.Debugw("getblocks: error getting block by cid", "cid", cid, "error", err) - return - } - - select { - case bCh <- block: - case <-ctx.Done(): - return - } - }(c) - } - wg.Wait() - close(bCh) - }() - - return bCh -} diff --git a/share/eds/adapters_test.go b/share/eds/adapters_test.go deleted file mode 100644 index 70165b81c8..0000000000 --- a/share/eds/adapters_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package eds - -import ( - "context" - "errors" - mrand "math/rand" - "sort" - "testing" - "time" - - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-node/share/ipld" -) - -func TestBlockGetter_GetBlocks(t *testing.T) { - t.Run("happy path", func(t *testing.T) { - cids := randCIDs(t, 32) - // sort cids in asc order - sort.Slice(cids, func(i, j int) bool { - return cids[i].String() < cids[j].String() - }) - - bg := &BlockGetter{store: rbsMock{}} - blocksCh := bg.GetBlocks(context.Background(), cids) - - // collect blocks from channel - blocks := make([]blocks.Block, 0, len(cids)) - for block := range blocksCh { - blocks = append(blocks, block) - } - - // sort blocks in cid asc order - sort.Slice(blocks, func(i, j int) bool { - return blocks[i].Cid().String() < blocks[j].Cid().String() - }) - - // validate results - require.Equal(t, len(cids), len(blocks)) - for i, block := range blocks { - require.Equal(t, cids[i].String(), block.Cid().String()) - } - }) - t.Run("retrieval error", func(t *testing.T) { - cids := randCIDs(t, 32) - - // split cids into failed and succeeded - failedLen := mrand.Intn(len(cids)-1) + 1 - failed := make(map[cid.Cid]struct{}, failedLen) - succeeded := make([]cid.Cid, 0, len(cids)-failedLen) - for i, cid := range cids { - if i < failedLen { - failed[cid] = struct{}{} - continue - } - succeeded = append(succeeded, cid) - } - - // sort succeeded cids in asc order - sort.Slice(succeeded, func(i, j int) bool { - return succeeded[i].String() < succeeded[j].String() - }) - - bg := &BlockGetter{store: rbsMock{failed: failed}} - blocksCh := bg.GetBlocks(context.Background(), cids) - - // collect blocks from channel - blocks := make([]blocks.Block, 0, len(cids)) - for block := range blocksCh { - blocks = append(blocks, block) - } - - // sort blocks in cid asc order - sort.Slice(blocks, func(i, j int) bool { - return blocks[i].Cid().String() < blocks[j].Cid().String() - }) - - // validate results - require.Equal(t, len(succeeded), len(blocks)) - for i, block := range blocks { - require.Equal(t, succeeded[i].String(), block.Cid().String()) - } - }) - t.Run("retrieval timeout", func(t *testing.T) { - cids := randCIDs(t, 128) - - bg := &BlockGetter{ - store: rbsMock{}, - } - - // cancel the context before any blocks are collected - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - blocksCh := bg.GetBlocks(ctx, cids) - - // pretend nobody is reading from blocksCh after context is canceled - time.Sleep(50 * time.Millisecond) - - // blocksCh should be closed indicating GetBlocks exited - select { - case _, ok := <-blocksCh: - require.False(t, ok) - default: - t.Error("channel is not closed on canceled context") - } - }) -} - -// rbsMock is a dagstore.ReadBlockstore mock -type rbsMock struct { - failed map[cid.Cid]struct{} -} - -func (r rbsMock) Has(context.Context, cid.Cid) (bool, error) { - panic("implement me") -} - -func (r rbsMock) Get(_ context.Context, cid cid.Cid) (blocks.Block, error) { - // return error for failed items - if _, ok := r.failed[cid]; ok { - return nil, errors.New("not found") - } - - return blocks.NewBlockWithCid(nil, cid) -} - -func (r rbsMock) GetSize(context.Context, cid.Cid) (int, error) { - panic("implement me") -} - -func (r rbsMock) AllKeysChan(context.Context) (<-chan cid.Cid, error) { - panic("implement me") -} - -func (r rbsMock) HashOnRead(bool) { - panic("implement me") -} - -func randCIDs(t *testing.T, n int) []cid.Cid { - cids := make([]cid.Cid, n) - for i := range cids { - cids[i] = ipld.RandNamespacedCID(t) - } - return cids -} diff --git a/share/eds/axis_half.go b/share/eds/axis_half.go new file mode 100644 index 0000000000..83b1f4d2f3 --- /dev/null +++ b/share/eds/axis_half.go @@ -0,0 +1,77 @@ +package eds + +import ( + "fmt" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var codec = share.DefaultRSMT2DCodec() + +// AxisHalf represents a half of data for a row or column in the EDS. +type AxisHalf struct { + Shares []libshare.Share + // IsParity indicates whether the half is parity or data. + IsParity bool +} + +// ToRow converts the AxisHalf to a shwap.Row. +func (a AxisHalf) ToRow() shwap.Row { + side := shwap.Left + if a.IsParity { + side = shwap.Right + } + return shwap.NewRow(a.Shares, side) +} + +// Extended returns full axis shares from half axis shares. +func (a AxisHalf) Extended() ([]libshare.Share, error) { + if a.IsParity { + return reconstructShares(a.Shares) + } + return extendShares(a.Shares) +} + +// extendShares constructs full axis shares from original half axis shares. +func extendShares(original []libshare.Share) ([]libshare.Share, error) { + if len(original) == 0 { + return nil, fmt.Errorf("original shares are empty") + } + + parity, err := codec.Encode(libshare.ToBytes(original)) + if err != nil { + return nil, fmt.Errorf("encoding: %w", err) + } + + parityShrs, err := libshare.FromBytes(parity) + if err != nil { + return nil, err + } + + sqLen := len(original) * 2 + shares := make([]libshare.Share, sqLen) + copy(shares, original) + copy(shares[sqLen/2:], parityShrs) + return shares, nil +} + +func reconstructShares(parity []libshare.Share) ([]libshare.Share, error) { + if len(parity) == 0 { + return nil, fmt.Errorf("parity shares are empty") + } + + sqLen := len(parity) * 2 + shares := make([]libshare.Share, sqLen) + for i := sqLen / 2; i < sqLen; i++ { + shares[i] = parity[i-sqLen/2] + } + shrs, err := codec.Decode(libshare.ToBytes(shares)) + if err != nil { + return nil, fmt.Errorf("reconstructing: %w", err) + } + + return libshare.FromBytes(shrs) +} diff --git a/share/eds/axis_half_test.go b/share/eds/axis_half_test.go new file mode 100644 index 0000000000..643dd85af3 --- /dev/null +++ b/share/eds/axis_half_test.go @@ -0,0 +1,33 @@ +package eds + +import ( + "testing" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +func TestExtendAxisHalf(t *testing.T) { + shares, err := libshare.RandShares(16) + require.NoError(t, err) + + original := AxisHalf{ + Shares: shares, + IsParity: false, + } + + extended, err := original.Extended() + require.NoError(t, err) + require.Len(t, extended, len(shares)*2) + + parity := AxisHalf{ + Shares: extended[len(shares):], + IsParity: true, + } + + parityExtended, err := parity.Extended() + require.NoError(t, err) + + require.Equal(t, extended, parityExtended) +} diff --git a/share/eds/blockstore.go b/share/eds/blockstore.go deleted file mode 100644 index 5ae109bf29..0000000000 --- a/share/eds/blockstore.go +++ /dev/null @@ -1,156 +0,0 @@ -package eds - -import ( - "context" - "errors" - "fmt" - - bstore "github.com/ipfs/boxo/blockstore" - "github.com/ipfs/boxo/datastore/dshelp" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - "github.com/ipfs/go-datastore/namespace" - ipld "github.com/ipfs/go-ipld-format" - - share_ipld "github.com/celestiaorg/celestia-node/share/ipld" -) - -var _ bstore.Blockstore = (*blockstore)(nil) - -var ( - blockstoreCacheKey = datastore.NewKey("bs-cache") - errUnsupportedOperation = errors.New("unsupported operation") -) - -// blockstore implements the store.Blockstore interface on an EDSStore. -// The lru cache approach is heavily inspired by the existing implementation upstream. -// We simplified the design to not support multiple shards per key, call GetSize directly on the -// underlying RO blockstore, and do not throw errors on Put/PutMany. Also, we do not abstract away -// the blockstore operations. -// -// The intuition here is that each CAR file is its own blockstore, so we need this top level -// implementation to allow for the blockstore operations to be routed to the underlying stores. -type blockstore struct { - store *Store - ds datastore.Batching -} - -func newBlockstore(store *Store, ds datastore.Batching) *blockstore { - return &blockstore{ - store: store, - ds: namespace.Wrap(ds, blockstoreCacheKey), - } -} - -func (bs *blockstore) Has(ctx context.Context, cid cid.Cid) (bool, error) { - keys, err := bs.store.dgstr.ShardsContainingMultihash(ctx, cid.Hash()) - if errors.Is(err, ErrNotFound) || errors.Is(err, ErrNotFoundInIndex) { - // key wasn't found in top level blockstore, but could be in datastore while being reconstructed - dsHas, dsErr := bs.ds.Has(ctx, dshelp.MultihashToDsKey(cid.Hash())) - if dsErr != nil { - return false, nil //nolint:nilerr // return false if error - } - return dsHas, nil - } - if err != nil { - return false, err - } - - return len(keys) > 0, nil -} - -func (bs *blockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) { - blockstr, err := bs.getReadOnlyBlockstore(ctx, cid) - if err == nil { - defer closeAndLog("blockstore", blockstr) - return blockstr.Get(ctx, cid) - } - - if errors.Is(err, ErrNotFound) || errors.Is(err, ErrNotFoundInIndex) { - k := dshelp.MultihashToDsKey(cid.Hash()) - blockData, err := bs.ds.Get(ctx, k) - if err == nil { - return blocks.NewBlockWithCid(blockData, cid) - } - // nmt's GetNode expects an ipld.ErrNotFound when a cid is not found. - return nil, ipld.ErrNotFound{Cid: cid} - } - - log.Debugf("failed to get blockstore for cid %s: %s", cid, err) - return nil, err -} - -func (bs *blockstore) GetSize(context.Context, cid.Cid) (int, error) { - // For now we return a fixed result, which is a max of possible values (see above). - // Motivation behind such behavior is described here: - // https://github.com/celestiaorg/celestia-node/issues/3630 - return share_ipld.LeafNodeSize, nil -} - -func (bs *blockstore) DeleteBlock(ctx context.Context, cid cid.Cid) error { - k := dshelp.MultihashToDsKey(cid.Hash()) - return bs.ds.Delete(ctx, k) -} - -func (bs *blockstore) Put(ctx context.Context, blk blocks.Block) error { - k := dshelp.MultihashToDsKey(blk.Cid().Hash()) - // note: we leave duplicate resolution to the underlying datastore - return bs.ds.Put(ctx, k, blk.RawData()) -} - -func (bs *blockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { - if len(blocks) == 1 { - // performance fast-path - return bs.Put(ctx, blocks[0]) - } - - t, err := bs.ds.Batch(ctx) - if err != nil { - return err - } - for _, b := range blocks { - k := dshelp.MultihashToDsKey(b.Cid().Hash()) - err = t.Put(ctx, k, b.RawData()) - if err != nil { - return err - } - } - return t.Commit(ctx) -} - -// AllKeysChan is a noop on the EDS blockstore because the keys are not stored in a single CAR file. -func (bs *blockstore) AllKeysChan(context.Context) (<-chan cid.Cid, error) { - return nil, errUnsupportedOperation -} - -// HashOnRead is a noop on the EDS blockstore but an error cannot be returned due to the method -// signature from the blockstore interface. -func (bs *blockstore) HashOnRead(bool) { - log.Warnf("HashOnRead is a noop on the EDS blockstore") -} - -// getReadOnlyBlockstore finds the underlying blockstore of the shard that contains the given CID. -func (bs *blockstore) getReadOnlyBlockstore(ctx context.Context, cid cid.Cid) (*BlockstoreCloser, error) { - keys, err := bs.store.dgstr.ShardsContainingMultihash(ctx, cid.Hash()) - if errors.Is(err, datastore.ErrNotFound) || errors.Is(err, ErrNotFoundInIndex) { - return nil, ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("failed to find shards containing multihash: %w", err) - } - - // check if either cache contains an accessor - shardKey := keys[0] - accessor, err := bs.store.cache.Load().Get(shardKey) - if err == nil { - return blockstoreCloser(accessor) - } - - // load accessor to the blockstore cache and use it as blockstoreCloser - accessor, err = bs.store.cache.Load().Second().GetOrLoad(ctx, shardKey, bs.store.getAccessor) - if err != nil { - return nil, fmt.Errorf("failed to get accessor for shard %s: %w", shardKey, err) - } - return blockstoreCloser(accessor) -} diff --git a/share/eds/blockstore_test.go b/share/eds/blockstore_test.go deleted file mode 100644 index d9dbf7ed30..0000000000 --- a/share/eds/blockstore_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package eds - -import ( - "context" - "io" - "testing" - - "github.com/filecoin-project/dagstore" - ipld "github.com/ipfs/go-ipld-format" - "github.com/ipld/go-car" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - ipld2 "github.com/celestiaorg/celestia-node/share/ipld" -) - -// TestBlockstore_Operations tests Has, Get, and GetSize on the top level eds.Store blockstore. -// It verifies that these operations are valid and successful on all blocks stored in a CAR file. -func TestBlockstore_Operations(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - edsStore, err := newStore(t) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - r, err := edsStore.GetCAR(ctx, dah.Hash()) - require.NoError(t, err) - carReader, err := car.NewCarReader(r) - require.NoError(t, err) - - topLevelBS := edsStore.Blockstore() - carBS, err := edsStore.CARBlockstore(ctx, dah.Hash()) - require.NoError(t, err) - defer func() { - require.NoError(t, carBS.Close()) - }() - - root, err := edsStore.GetDAH(ctx, dah.Hash()) - require.NoError(t, err) - require.True(t, dah.Equals(root)) - - blockstores := []dagstore.ReadBlockstore{topLevelBS, carBS} - - for { - next, err := carReader.Next() - if err != nil { - require.ErrorIs(t, err, io.EOF) - break - } - blockCid := next.Cid() - randomCid := ipld2.RandNamespacedCID(t) - - for _, bs := range blockstores { - // test GetSize - has, err := bs.Has(ctx, blockCid) - require.NoError(t, err, "blockstore.Has could not find root CID") - require.True(t, has) - - // test GetSize - block, err := bs.Get(ctx, blockCid) - assert.NoError(t, err, "blockstore.Get could not get a leaf CID") - assert.Equal(t, block.Cid(), blockCid) - assert.Equal(t, block.RawData(), next.RawData()) - - // test Get (cid not found) - _, err = bs.Get(ctx, randomCid) - require.ErrorAs(t, err, &ipld.ErrNotFound{Cid: randomCid}) - - // test GetSize - size, err := bs.GetSize(ctx, blockCid) - assert.NotZerof(t, size, "blocksize.GetSize reported a root block from blockstore was empty") - assert.NoError(t, err) - } - } -} diff --git a/share/eds/byzantine/bad_encoding.go b/share/eds/byzantine/bad_encoding.go index 7e1386a863..ab07c335e4 100644 --- a/share/eds/byzantine/bad_encoding.go +++ b/share/eds/byzantine/bad_encoding.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" "github.com/celestiaorg/go-fraud" "github.com/celestiaorg/rsmt2d" @@ -88,10 +88,14 @@ func (p *BadEncodingProof) UnmarshalBinary(data []byte) error { if err := in.Unmarshal(data); err != nil { return err } + sh, err := ProtoToShare(in.Shares) + if err != nil { + return err + } befp := &BadEncodingProof{ headerHash: in.HeaderHash, BlockHeight: in.Height, - Shares: ProtoToShare(in.Shares), + Shares: sh, Index: in.Index, Axis: rsmt2d.Axis(in.Axis), } @@ -184,7 +188,7 @@ func (p *BadEncodingProof) Validate(hdr *header.ExtendedHeader) error { log.Debugf("%s: %s at index %d", invalidProofPrefix, errIncorrectShare, index) return errIncorrectShare } - shares[index] = shr.Share + shares[index] = shr.Share.ToBytes() } codec := share.DefaultRSMT2DCodec() diff --git a/share/eds/byzantine/bad_encoding_test.go b/share/eds/byzantine/bad_encoding_test.go index 59ac24ad55..6b3940ba4a 100644 --- a/share/eds/byzantine/bad_encoding_test.go +++ b/share/eds/byzantine/bad_encoding_test.go @@ -13,8 +13,8 @@ import ( "github.com/stretchr/testify/require" core "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/celestia-app/v2/test/util/malicious" + "github.com/celestiaorg/celestia-app/v3/test/util/malicious" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" @@ -22,7 +22,6 @@ import ( "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds/edstest" "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/sharetest" ) func TestBEFP_Validate(t *testing.T) { @@ -30,17 +29,17 @@ func TestBEFP_Validate(t *testing.T) { defer t.Cleanup(cancel) bServ := ipld.NewMemBlockservice() - square := edstest.RandByzantineEDS(t, 16) - dah, err := da.NewDataAvailabilityHeader(square) + byzSquare := edstest.RandByzantineEDS(t, 16) + roots, err := share.NewAxisRoots(byzSquare) require.NoError(t, err) - err = ipld.ImportEDS(ctx, square, bServ) + err = ipld.ImportEDS(ctx, byzSquare, bServ) require.NoError(t, err) var errRsmt2d *rsmt2d.ErrByzantineData - err = square.Repair(dah.RowRoots, dah.ColumnRoots) + err = byzSquare.Repair(roots.RowRoots, roots.ColumnRoots) require.ErrorAs(t, err, &errRsmt2d) - byzantine := NewErrByzantine(ctx, bServ.Blockstore(), &dah, errRsmt2d) + byzantine := NewErrByzantine(ctx, bServ.Blockstore(), roots, errRsmt2d) var errByz *ErrByzantine require.ErrorAs(t, byzantine, &errByz) @@ -55,7 +54,7 @@ func TestBEFP_Validate(t *testing.T) { { name: "valid BEFP", prepareFn: func() error { - return proof.Validate(&header.ExtendedHeader{DAH: &dah}) + return proof.Validate(&header.ExtendedHeader{DAH: roots}) }, expectedResult: func(err error) { require.NoError(t, err) @@ -65,12 +64,12 @@ func TestBEFP_Validate(t *testing.T) { name: "invalid BEFP for valid header", prepareFn: func() error { validSquare := edstest.RandEDS(t, 2) - validDah, err := da.NewDataAvailabilityHeader(validSquare) + validRoots, err := share.NewAxisRoots(validSquare) require.NoError(t, err) err = ipld.ImportEDS(ctx, validSquare, bServ) require.NoError(t, err) validShares := validSquare.Flattened() - errInvalidByz := NewErrByzantine(ctx, bServ.Blockstore(), &validDah, + errInvalidByz := NewErrByzantine(ctx, bServ.Blockstore(), validRoots, &rsmt2d.ErrByzantineData{ Axis: rsmt2d.Row, Index: 0, @@ -80,7 +79,7 @@ func TestBEFP_Validate(t *testing.T) { var errInvalid *ErrByzantine require.ErrorAs(t, errInvalidByz, &errInvalid) invalidBefp := CreateBadEncodingProof([]byte("hash"), 0, errInvalid) - return invalidBefp.Validate(&header.ExtendedHeader{DAH: &validDah}) + return invalidBefp.Validate(&header.ExtendedHeader{DAH: validRoots}) }, expectedResult: func(err error) { require.ErrorIs(t, err, errNMTTreeRootsMatch) @@ -90,10 +89,11 @@ func TestBEFP_Validate(t *testing.T) { name: "incorrect share with Proof", prepareFn: func() error { // break the first shareWithProof to test negative case - sh := sharetest.RandShares(t, 2) + sh, err := libshare.RandShares(2) + require.NoError(t, err) nmtProof := nmt.NewInclusionProof(0, 1, nil, false) befp.Shares[0] = &ShareWithProof{sh[0], &nmtProof, rsmt2d.Row} - return proof.Validate(&header.ExtendedHeader{DAH: &dah}) + return proof.Validate(&header.ExtendedHeader{DAH: roots}) }, expectedResult: func(err error) { require.ErrorIs(t, err, errIncorrectShare) @@ -103,7 +103,7 @@ func TestBEFP_Validate(t *testing.T) { name: "invalid amount of shares", prepareFn: func() error { befp.Shares = befp.Shares[0 : len(befp.Shares)/2] - return proof.Validate(&header.ExtendedHeader{DAH: &dah}) + return proof.Validate(&header.ExtendedHeader{DAH: roots}) }, expectedResult: func(err error) { require.ErrorIs(t, err, errIncorrectAmountOfShares) @@ -113,7 +113,7 @@ func TestBEFP_Validate(t *testing.T) { name: "not enough shares to recompute the root", prepareFn: func() error { befp.Shares[0] = nil - return proof.Validate(&header.ExtendedHeader{DAH: &dah}) + return proof.Validate(&header.ExtendedHeader{DAH: roots}) }, expectedResult: func(err error) { require.ErrorIs(t, err, errIncorrectAmountOfShares) @@ -123,7 +123,7 @@ func TestBEFP_Validate(t *testing.T) { name: "index out of bounds", prepareFn: func() error { befp.Index = 100 - return proof.Validate(&header.ExtendedHeader{DAH: &dah}) + return proof.Validate(&header.ExtendedHeader{DAH: roots}) }, expectedResult: func(err error) { require.ErrorIs(t, err, errIncorrectIndex) @@ -136,7 +136,7 @@ func TestBEFP_Validate(t *testing.T) { RawHeader: core.Header{ Height: 42, }, - DAH: &dah, + DAH: roots, }) }, expectedResult: func(err error) { @@ -161,19 +161,19 @@ func TestIncorrectBadEncodingFraudProof(t *testing.T) { bServ := ipld.NewMemBlockservice() squareSize := 8 - shares := sharetest.RandShares(t, squareSize*squareSize) - + shares, err := libshare.RandShares(squareSize * squareSize) + require.NoError(t, err) eds, err := ipld.AddShares(ctx, shares, bServ) require.NoError(t, err) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) // get an arbitrary row rowIdx := squareSize / 2 shareProofs := make([]*ShareWithProof, 0, eds.Width()) for i := range shareProofs { - proof, err := GetShareWithProof(ctx, bServ, dah, shares[i], rsmt2d.Row, rowIdx, i) + proof, err := GetShareWithProof(ctx, bServ, roots, shares[i], rsmt2d.Row, rowIdx, i) require.NoError(t, err) shareProofs = append(shareProofs, proof) } @@ -189,7 +189,7 @@ func TestIncorrectBadEncodingFraudProof(t *testing.T) { RawHeader: core.Header{ Height: 420, }, - DAH: dah, + DAH: roots, Commit: &core.Commit{ BlockID: core.BlockID{ Hash: []byte("made up hash"), @@ -221,22 +221,22 @@ func TestBEFP_ValidateOutOfOrderShares(t *testing.T) { ) require.NoError(t, err, "failure to recompute the extended data square") - dah, err := da.NewDataAvailabilityHeader(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) var errRsmt2d *rsmt2d.ErrByzantineData - err = eds.Repair(dah.RowRoots, dah.ColumnRoots) + err = eds.Repair(roots.RowRoots, roots.ColumnRoots) require.ErrorAs(t, err, &errRsmt2d) err = batchAddr.Commit() require.NoError(t, err) - byzantine := NewErrByzantine(ctx, bServ.Blockstore(), &dah, errRsmt2d) + byzantine := NewErrByzantine(ctx, bServ.Blockstore(), roots, errRsmt2d) var errByz *ErrByzantine require.ErrorAs(t, byzantine, &errByz) befp := CreateBadEncodingProof([]byte("hash"), 0, errByz) - err = befp.Validate(&header.ExtendedHeader{DAH: &dah}) + err = befp.Validate(&header.ExtendedHeader{DAH: roots}) require.NoError(t, err) } @@ -253,7 +253,7 @@ func newNamespacedBlockService() *namespacedBlockService { sha256NamespaceFlagged := uint64(0x7701) // register the nmt hasher to validate the order of namespaces mhcore.Register(sha256NamespaceFlagged, func() hash.Hash { - nh := nmt.NewNmtHasher(share.NewSHA256Hasher(), share.NamespaceSize, true) + nh := nmt.NewNmtHasher(share.NewSHA256Hasher(), libshare.NamespaceSize, true) nh.Reset() return nh }) @@ -266,7 +266,7 @@ func newNamespacedBlockService() *namespacedBlockService { Codec: sha256NamespaceFlagged, MhType: sha256NamespaceFlagged, // equals to NmtHasher.Size() - MhLength: share.NewSHA256Hasher().Size() + 2*share.NamespaceSize, + MhLength: share.NewSHA256Hasher().Size() + 2*libshare.NamespaceSize, } return bs } diff --git a/share/eds/byzantine/byzantine.go b/share/eds/byzantine/byzantine.go index 44307138c5..28c0bf2413 100644 --- a/share/eds/byzantine/byzantine.go +++ b/share/eds/byzantine/byzantine.go @@ -6,9 +6,10 @@ import ( "github.com/ipfs/boxo/blockstore" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" + "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/ipld" ) @@ -32,17 +33,22 @@ func (e *ErrByzantine) Error() string { func NewErrByzantine( ctx context.Context, bStore blockstore.Blockstore, - dah *da.DataAvailabilityHeader, + roots *share.AxisRoots, errByz *rsmt2d.ErrByzantineData, ) error { sharesWithProof := make([]*ShareWithProof, len(errByz.Shares)) bGetter := ipld.NewBlockservice(bStore, nil) var count int - for index, share := range errByz.Shares { - if len(share) == 0 { + for index, shr := range errByz.Shares { + if len(shr) == 0 { continue } - swp, err := GetShareWithProof(ctx, bGetter, dah, share, errByz.Axis, int(errByz.Index), index) + sh, err := libshare.NewShare(shr) + if err != nil { + log.Warn("failed to create share", "index", index, "err", err) + continue + } + swp, err := GetShareWithProof(ctx, bGetter, roots, *sh, errByz.Axis, int(errByz.Index), index) if err != nil { log.Warn("requesting proof failed", "errByz", errByz, @@ -53,12 +59,12 @@ func NewErrByzantine( sharesWithProof[index] = swp // it is enough to collect half of the shares to construct the befp - if count++; count >= len(dah.RowRoots)/2 { + if count++; count >= len(roots.RowRoots)/2 { break } } - if count < len(dah.RowRoots)/2 { + if count < len(roots.RowRoots)/2 { return fmt.Errorf("failed to collect proof") } diff --git a/share/eds/byzantine/share_proof.go b/share/eds/byzantine/share_proof.go index d064656830..4b3ae592db 100644 --- a/share/eds/byzantine/share_proof.go +++ b/share/eds/byzantine/share_proof.go @@ -3,12 +3,11 @@ package byzantine import ( "context" "errors" - "math" "github.com/ipfs/boxo/blockservice" - "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" nmt_pb "github.com/celestiaorg/nmt/pb" "github.com/celestiaorg/rsmt2d" @@ -23,7 +22,7 @@ var log = logging.Logger("share/byzantine") // ShareWithProof contains data with corresponding Merkle Proof type ShareWithProof struct { // Share is a full data including namespace - share.Share + libshare.Share // Proof is a Merkle Proof of current share Proof *nmt.Proof // Axis is a proof axis @@ -31,25 +30,25 @@ type ShareWithProof struct { } // Validate validates inclusion of the share under the given root CID. -func (s *ShareWithProof) Validate(dah *share.Root, axisType rsmt2d.Axis, axisIdx, shrIdx int) bool { +func (s *ShareWithProof) Validate(roots *share.AxisRoots, axisType rsmt2d.Axis, axisIdx, shrIdx int) bool { var rootHash []byte switch axisType { case rsmt2d.Row: - rootHash = rootHashForCoordinates(dah, s.Axis, shrIdx, axisIdx) + rootHash = rootHashForCoordinates(roots, s.Axis, shrIdx, axisIdx) case rsmt2d.Col: - rootHash = rootHashForCoordinates(dah, s.Axis, axisIdx, shrIdx) + rootHash = rootHashForCoordinates(roots, s.Axis, axisIdx, shrIdx) } - edsSize := len(dah.RowRoots) + edsSize := len(roots.RowRoots) isParity := shrIdx >= edsSize/2 || axisIdx >= edsSize/2 - namespace := share.ParitySharesNamespace + namespace := libshare.ParitySharesNamespace if !isParity { - namespace = share.GetNamespace(s.Share) + namespace = s.Share.Namespace() } return s.Proof.VerifyInclusion( share.NewSHA256Hasher(), - namespace.ToNMT(), - [][]byte{s.Share}, + namespace.Bytes(), + [][]byte{s.Share.ToBytes()}, rootHash, ) } @@ -58,9 +57,8 @@ func (s *ShareWithProof) ShareWithProofToProto() *pb.Share { if s == nil { return &pb.Share{} } - return &pb.Share{ - Data: s.Share, + Data: s.Share.ToBytes(), Proof: &nmt_pb.Proof{ Start: int64(s.Proof.Start()), End: int64(s.Proof.End()), @@ -77,33 +75,31 @@ func (s *ShareWithProof) ShareWithProofToProto() *pb.Share { func GetShareWithProof( ctx context.Context, bGetter blockservice.BlockGetter, - dah *share.Root, - share share.Share, + roots *share.AxisRoots, + share libshare.Share, axisType rsmt2d.Axis, axisIdx, shrIdx int, ) (*ShareWithProof, error) { if axisType == rsmt2d.Col { axisIdx, shrIdx, axisType = shrIdx, axisIdx, rsmt2d.Row } - width := len(dah.RowRoots) + width := len(roots.RowRoots) // try row proofs - root := dah.RowRoots[axisIdx] - rootCid := ipld.MustCidFromNamespacedSha256(root) - proof, err := getProofsAt(ctx, bGetter, rootCid, shrIdx, width) + root := roots.RowRoots[axisIdx] + proof, err := ipld.GetProof(ctx, bGetter, root, shrIdx, width) if err == nil { shareWithProof := &ShareWithProof{ Share: share, Proof: &proof, Axis: rsmt2d.Row, } - if shareWithProof.Validate(dah, axisType, axisIdx, shrIdx) { + if shareWithProof.Validate(roots, axisType, axisIdx, shrIdx) { return shareWithProof, nil } } // try column proofs - root = dah.ColumnRoots[shrIdx] - rootCid = ipld.MustCidFromNamespacedSha256(root) - proof, err = getProofsAt(ctx, bGetter, rootCid, axisIdx, width) + root = roots.ColumnRoots[shrIdx] + proof, err = ipld.GetProof(ctx, bGetter, root, axisIdx, width) if err != nil { return nil, err } @@ -112,48 +108,30 @@ func GetShareWithProof( Proof: &proof, Axis: rsmt2d.Col, } - if shareWithProof.Validate(dah, axisType, axisIdx, shrIdx) { + if shareWithProof.Validate(roots, axisType, axisIdx, shrIdx) { return shareWithProof, nil } return nil, errors.New("failed to collect proof") } -func getProofsAt( - ctx context.Context, - bGetter blockservice.BlockGetter, - root cid.Cid, - index, - total int, -) (nmt.Proof, error) { - proofPath := make([]cid.Cid, 0, int(math.Sqrt(float64(total)))) - proofPath, err := ipld.GetProof(ctx, bGetter, root, proofPath, index, total) - if err != nil { - return nmt.Proof{}, err - } - - rangeProofs := make([][]byte, 0, len(proofPath)) - for i := len(proofPath) - 1; i >= 0; i-- { - node := ipld.NamespacedSha256FromCID(proofPath[i]) - rangeProofs = append(rangeProofs, node) - } - - return nmt.NewInclusionProof(index, index+1, rangeProofs, true), nil -} - -func ProtoToShare(protoShares []*pb.Share) []*ShareWithProof { +func ProtoToShare(protoShares []*pb.Share) ([]*ShareWithProof, error) { shares := make([]*ShareWithProof, len(protoShares)) - for i, share := range protoShares { - if share.Proof == nil { + for i, protoSh := range protoShares { + if protoSh.Proof == nil { continue } - proof := ProtoToProof(share.Proof) + proof := ProtoToProof(protoSh.Proof) + sh, err := libshare.NewShare(protoSh.Data) + if err != nil { + return nil, err + } shares[i] = &ShareWithProof{ - Share: share.Data, + Share: *sh, Proof: &proof, - Axis: rsmt2d.Axis(share.ProofAxis), + Axis: rsmt2d.Axis(protoSh.ProofAxis), } } - return shares + return shares, nil } func ProtoToProof(protoProof *nmt_pb.Proof) nmt.Proof { @@ -165,7 +143,7 @@ func ProtoToProof(protoProof *nmt_pb.Proof) nmt.Proof { ) } -func rootHashForCoordinates(r *share.Root, axisType rsmt2d.Axis, x, y int) []byte { +func rootHashForCoordinates(r *share.AxisRoots, axisType rsmt2d.Axis, x, y int) []byte { if axisType == rsmt2d.Row { return r.RowRoots[y] } diff --git a/share/eds/byzantine/share_proof_test.go b/share/eds/byzantine/share_proof_test.go deleted file mode 100644 index 2b5cb57bb9..0000000000 --- a/share/eds/byzantine/share_proof_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package byzantine - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/sharetest" -) - -func TestGetProof(t *testing.T) { - const width = 8 - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - defer cancel() - bServ := ipld.NewMemBlockservice() - - shares := sharetest.RandShares(t, width*width) - in, err := ipld.AddShares(ctx, shares, bServ) - require.NoError(t, err) - - dah, err := da.NewDataAvailabilityHeader(in) - require.NoError(t, err) - - for _, proofType := range []rsmt2d.Axis{rsmt2d.Row, rsmt2d.Col} { - var roots [][]byte - switch proofType { - case rsmt2d.Row: - roots = dah.RowRoots - case rsmt2d.Col: - roots = dah.ColumnRoots - } - for axisIdx := 0; axisIdx < width*2; axisIdx++ { - rootCid := ipld.MustCidFromNamespacedSha256(roots[axisIdx]) - for shrIdx := 0; shrIdx < width*2; shrIdx++ { - proof, err := getProofsAt(ctx, bServ, rootCid, shrIdx, int(in.Width())) - require.NoError(t, err) - node, err := ipld.GetLeaf(ctx, bServ, rootCid, shrIdx, int(in.Width())) - require.NoError(t, err) - inclusion := &ShareWithProof{ - Share: share.GetData(node.RawData()), - Proof: &proof, - Axis: proofType, - } - require.True(t, inclusion.Validate(&dah, proofType, axisIdx, shrIdx)) - // swap axis indexes to test if validation still works against the orthogonal coordinate - switch proofType { - case rsmt2d.Row: - require.True(t, inclusion.Validate(&dah, rsmt2d.Col, shrIdx, axisIdx)) - case rsmt2d.Col: - require.True(t, inclusion.Validate(&dah, rsmt2d.Row, shrIdx, axisIdx)) - } - } - } - } -} diff --git a/share/eds/cache/accessor_cache.go b/share/eds/cache/accessor_cache.go deleted file mode 100644 index e7ac043426..0000000000 --- a/share/eds/cache/accessor_cache.go +++ /dev/null @@ -1,267 +0,0 @@ -package cache - -import ( - "context" - "errors" - "fmt" - "io" - "sync" - "sync/atomic" - "time" - - "github.com/filecoin-project/dagstore" - "github.com/filecoin-project/dagstore/shard" - lru "github.com/hashicorp/golang-lru/v2" -) - -const defaultCloseTimeout = time.Minute - -var _ Cache = (*AccessorCache)(nil) - -// AccessorCache implements the Cache interface using an LRU cache backend. -type AccessorCache struct { - // The name is a prefix that will be used for cache metrics if they are enabled. - name string - // stripedLocks prevents simultaneous RW access to the blockstore cache for a shard. Instead - // of using only one lock or one lock per key, we stripe the shard keys across 256 locks. 256 is - // chosen because it 0-255 is the range of values we get looking at the last byte of the key. - stripedLocks [256]sync.Mutex - // Caches the blockstore for a given shard for shard read affinity, i.e., further reads will likely - // be from the same shard. Maps (shard key -> blockstore). - cache *lru.Cache[shard.Key, *accessorWithBlockstore] - - metrics *metrics -} - -// accessorWithBlockstore is the value that we store in the blockstore Cache. It implements the -// Accessor interface. -type accessorWithBlockstore struct { - sync.RWMutex - shardAccessor Accessor - // The blockstore is stored separately because each access to the blockstore over the shard - // accessor reopens the underlying CAR. - bs dagstore.ReadBlockstore - - done chan struct{} - refs atomic.Int32 - isClosed bool -} - -// Blockstore implements the Blockstore of the Accessor interface. It creates the blockstore on the -// first request and reuses the created instance for all subsequent requests. -func (s *accessorWithBlockstore) Blockstore() (dagstore.ReadBlockstore, error) { - s.Lock() - defer s.Unlock() - var err error - if s.bs == nil { - s.bs, err = s.shardAccessor.Blockstore() - } - return s.bs, err -} - -// Reader returns a new copy of the reader to read data. -func (s *accessorWithBlockstore) Reader() io.Reader { - return s.shardAccessor.Reader() -} - -func (s *accessorWithBlockstore) addRef() error { - s.Lock() - defer s.Unlock() - if s.isClosed { - // item is already closed and soon will be removed after all refs are released - return errCacheMiss - } - if s.refs.Add(1) == 1 { - // there were no refs previously and done channel was closed, reopen it by recreating - s.done = make(chan struct{}) - } - return nil -} - -func (s *accessorWithBlockstore) removeRef() { - s.Lock() - defer s.Unlock() - if s.refs.Add(-1) <= 0 { - close(s.done) - } -} - -func (s *accessorWithBlockstore) close() error { - s.Lock() - if s.isClosed { - s.Unlock() - // accessor will be closed by another goroutine - return nil - } - s.isClosed = true - done := s.done - s.Unlock() - - // wait until all references are released or timeout is reached. If timeout is reached, log an - // error and close the accessor forcefully. - select { - case <-done: - case <-time.After(defaultCloseTimeout): - log.Errorf("closing accessor, some readers didn't close the accessor within timeout,"+ - " amount left: %v", s.refs.Load()) - } - if err := s.shardAccessor.Close(); err != nil { - return fmt.Errorf("closing accessor: %w", err) - } - return nil -} - -func NewAccessorCache(name string, cacheSize int) (*AccessorCache, error) { - bc := &AccessorCache{ - name: name, - } - // Instantiate the blockstore Cache. - bslru, err := lru.NewWithEvict[shard.Key, *accessorWithBlockstore](cacheSize, bc.evictFn()) - if err != nil { - return nil, fmt.Errorf("failed to instantiate blockstore cache: %w", err) - } - bc.cache = bslru - return bc, nil -} - -// evictFn will be invoked when an item is evicted from the cache. -func (bc *AccessorCache) evictFn() func(shard.Key, *accessorWithBlockstore) { - return func(_ shard.Key, abs *accessorWithBlockstore) { - // we can release accessor from cache early, while it is being closed in parallel routine - go func() { - err := abs.close() - if err != nil { - bc.metrics.observeEvicted(true) - log.Errorf("couldn't close accessor after cache eviction: %s", err) - return - } - bc.metrics.observeEvicted(false) - }() - } -} - -// Get retrieves the Accessor for a given shard key from the Cache. If the Accessor is not in -// the Cache, it returns an errCacheMiss. -func (bc *AccessorCache) Get(key shard.Key) (Accessor, error) { - lk := &bc.stripedLocks[shardKeyToStriped(key)] - lk.Lock() - defer lk.Unlock() - - accessor, err := bc.get(key) - if err != nil { - bc.metrics.observeGet(false) - return nil, err - } - bc.metrics.observeGet(true) - return newRefCloser(accessor) -} - -func (bc *AccessorCache) get(key shard.Key) (*accessorWithBlockstore, error) { - abs, ok := bc.cache.Get(key) - if !ok { - return nil, errCacheMiss - } - return abs, nil -} - -// GetOrLoad attempts to get an item from the cache, and if not found, invokes -// the provided loader function to load it. -func (bc *AccessorCache) GetOrLoad( - ctx context.Context, - key shard.Key, - loader func(context.Context, shard.Key) (Accessor, error), -) (Accessor, error) { - lk := &bc.stripedLocks[shardKeyToStriped(key)] - lk.Lock() - defer lk.Unlock() - - abs, err := bc.get(key) - if err == nil { - // return accessor, only of it is not closed yet - accessorWithRef, err := newRefCloser(abs) - if err == nil { - bc.metrics.observeGet(true) - return accessorWithRef, nil - } - } - - // accessor not found in cache, so load new one using loader - accessor, err := loader(ctx, key) - if err != nil { - return nil, fmt.Errorf("unable to load accessor: %w", err) - } - - abs = &accessorWithBlockstore{ - shardAccessor: accessor, - } - - // Create a new accessor first to increment the reference count in it, so it cannot get evicted - // from the inner lru cache before it is used. - accessorWithRef, err := newRefCloser(abs) - if err != nil { - return nil, err - } - bc.cache.Add(key, abs) - return accessorWithRef, nil -} - -// Remove removes the Accessor for a given key from the cache. -func (bc *AccessorCache) Remove(key shard.Key) error { - lk := &bc.stripedLocks[shardKeyToStriped(key)] - lk.Lock() - accessor, err := bc.get(key) - lk.Unlock() - if errors.Is(err, errCacheMiss) { - // item is not in cache - return nil - } - if err = accessor.close(); err != nil { - return err - } - // The cache will call evictFn on removal, where accessor close will be called. - bc.cache.Remove(key) - return nil -} - -// EnableMetrics enables metrics for the cache. -func (bc *AccessorCache) EnableMetrics() (CloseMetricsFn, error) { - var err error - bc.metrics, err = newMetrics(bc) - if err != nil { - return nil, err - } - return bc.metrics.close, err -} - -// refCloser manages references to accessor from provided reader and removes the ref, when the -// Close is called -type refCloser struct { - *accessorWithBlockstore - closeFn func() -} - -// newRefCloser creates new refCloser -func newRefCloser(abs *accessorWithBlockstore) (*refCloser, error) { - if err := abs.addRef(); err != nil { - return nil, err - } - - var closeOnce sync.Once - return &refCloser{ - accessorWithBlockstore: abs, - closeFn: func() { - closeOnce.Do(abs.removeRef) - }, - }, nil -} - -func (c *refCloser) Close() error { - c.closeFn() - return nil -} - -// shardKeyToStriped returns the index of the lock to use for a given shard key. We use the last -// byte of the shard key as the pseudo-random index. -func shardKeyToStriped(sk shard.Key) byte { - return sk.String()[len(sk.String())-1] -} diff --git a/share/eds/cache/cache.go b/share/eds/cache/cache.go deleted file mode 100644 index ff38faafba..0000000000 --- a/share/eds/cache/cache.go +++ /dev/null @@ -1,49 +0,0 @@ -package cache - -import ( - "context" - "errors" - "io" - - "github.com/filecoin-project/dagstore" - "github.com/filecoin-project/dagstore/shard" - logging "github.com/ipfs/go-log/v2" - "go.opentelemetry.io/otel" -) - -var ( - log = logging.Logger("share/eds/cache") - meter = otel.Meter("eds_store_cache") -) - -var errCacheMiss = errors.New("accessor not found in blockstore cache") - -type CloseMetricsFn func() error - -// Cache is an interface that defines the basic Cache operations. -type Cache interface { - // Get retrieves an item from the Cache. - Get(shard.Key) (Accessor, error) - - // GetOrLoad attempts to get an item from the Cache and, if not found, invokes - // the provided loader function to load it into the Cache. - GetOrLoad( - ctx context.Context, - key shard.Key, - loader func(context.Context, shard.Key) (Accessor, error), - ) (Accessor, error) - - // Remove removes an item from Cache. - Remove(shard.Key) error - - // EnableMetrics enables metrics in Cache - EnableMetrics() (CloseMetricsFn, error) -} - -// Accessor is a interface type returned by cache, that allows to read raw data by reader or create -// readblockstore -type Accessor interface { - Blockstore() (dagstore.ReadBlockstore, error) - Reader() io.Reader - io.Closer -} diff --git a/share/eds/cache/doublecache.go b/share/eds/cache/doublecache.go deleted file mode 100644 index a7f2a4871e..0000000000 --- a/share/eds/cache/doublecache.go +++ /dev/null @@ -1,62 +0,0 @@ -package cache - -import ( - "errors" - - "github.com/filecoin-project/dagstore/shard" -) - -// DoubleCache represents a Cache that looks into multiple caches one by one. -type DoubleCache struct { - first, second Cache -} - -// NewDoubleCache creates a new DoubleCache with the provided caches. -func NewDoubleCache(first, second Cache) *DoubleCache { - return &DoubleCache{ - first: first, - second: second, - } -} - -// Get looks for an item in all the caches one by one and returns the Cache found item. -func (mc *DoubleCache) Get(key shard.Key) (Accessor, error) { - ac, err := mc.first.Get(key) - if err == nil { - return ac, nil - } - return mc.second.Get(key) -} - -// Remove removes an item from all underlying caches -func (mc *DoubleCache) Remove(key shard.Key) error { - err1 := mc.first.Remove(key) - err2 := mc.second.Remove(key) - return errors.Join(err1, err2) -} - -func (mc *DoubleCache) First() Cache { - return mc.first -} - -func (mc *DoubleCache) Second() Cache { - return mc.second -} - -func (mc *DoubleCache) EnableMetrics() (CloseMetricsFn, error) { - firstCloser, err := mc.first.EnableMetrics() - if err != nil { - return nil, err - } - secondCloser, err := mc.second.EnableMetrics() - if err != nil { - return nil, err - } - - return func() error { - if err := errors.Join(firstCloser(), secondCloser()); err != nil { - log.Warnw("failed to close metrics", "err", err) - } - return nil - }, nil -} diff --git a/share/eds/cache/noop.go b/share/eds/cache/noop.go deleted file mode 100644 index 5d7444054e..0000000000 --- a/share/eds/cache/noop.go +++ /dev/null @@ -1,50 +0,0 @@ -package cache - -import ( - "context" - "io" - - "github.com/filecoin-project/dagstore" - "github.com/filecoin-project/dagstore/shard" -) - -var _ Cache = (*NoopCache)(nil) - -// NoopCache implements noop version of Cache interface -type NoopCache struct{} - -func (n NoopCache) Get(shard.Key) (Accessor, error) { - return nil, errCacheMiss -} - -func (n NoopCache) GetOrLoad( - context.Context, shard.Key, - func(context.Context, shard.Key) (Accessor, error), -) (Accessor, error) { - return NoopAccessor{}, nil -} - -func (n NoopCache) Remove(shard.Key) error { - return nil -} - -func (n NoopCache) EnableMetrics() (CloseMetricsFn, error) { - return func() error { return nil }, nil -} - -var _ Accessor = (*NoopAccessor)(nil) - -// NoopAccessor implements noop version of Accessor interface -type NoopAccessor struct{} - -func (n NoopAccessor) Blockstore() (dagstore.ReadBlockstore, error) { - return nil, nil //nolint:nilnil -} - -func (n NoopAccessor) Reader() io.Reader { - return nil -} - -func (n NoopAccessor) Close() error { - return nil -} diff --git a/share/eds/close_once.go b/share/eds/close_once.go new file mode 100644 index 0000000000..579b4b1e20 --- /dev/null +++ b/share/eds/close_once.go @@ -0,0 +1,101 @@ +package eds + +import ( + "context" + "errors" + "io" + "sync/atomic" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ AccessorStreamer = (*closeOnce)(nil) + +var errAccessorClosed = errors.New("accessor is closed") + +type closeOnce struct { + f AccessorStreamer + closed atomic.Bool +} + +func WithClosedOnce(f AccessorStreamer) AccessorStreamer { + return &closeOnce{f: f} +} + +func (c *closeOnce) Close() error { + if c.closed.Swap(true) { + return nil + } + err := c.f.Close() + // release reference to the accessor to allow GC to collect all resources associated with it + c.f = nil + return err +} + +func (c *closeOnce) Size(ctx context.Context) int { + if c.closed.Load() { + return 0 + } + return c.f.Size(ctx) +} + +func (c *closeOnce) DataHash(ctx context.Context) (share.DataHash, error) { + if c.closed.Load() { + return nil, errAccessorClosed + } + return c.f.DataHash(ctx) +} + +func (c *closeOnce) AxisRoots(ctx context.Context) (*share.AxisRoots, error) { + if c.closed.Load() { + return nil, errAccessorClosed + } + return c.f.AxisRoots(ctx) +} + +func (c *closeOnce) Sample(ctx context.Context, idx shwap.SampleCoords) (shwap.Sample, error) { + if c.closed.Load() { + return shwap.Sample{}, errAccessorClosed + } + return c.f.Sample(ctx, idx) +} + +func (c *closeOnce) AxisHalf( + ctx context.Context, + axisType rsmt2d.Axis, + axisIdx int, +) (AxisHalf, error) { + if c.closed.Load() { + return AxisHalf{}, errAccessorClosed + } + return c.f.AxisHalf(ctx, axisType, axisIdx) +} + +func (c *closeOnce) RowNamespaceData( + ctx context.Context, + namespace libshare.Namespace, + rowIdx int, +) (shwap.RowNamespaceData, error) { + if c.closed.Load() { + return shwap.RowNamespaceData{}, errAccessorClosed + } + return c.f.RowNamespaceData(ctx, namespace, rowIdx) +} + +func (c *closeOnce) Shares(ctx context.Context) ([]libshare.Share, error) { + if c.closed.Load() { + return nil, errAccessorClosed + } + return c.f.Shares(ctx) +} + +func (c *closeOnce) Reader() (io.Reader, error) { + if c.closed.Load() { + return nil, errAccessorClosed + } + return c.f.Reader() +} diff --git a/share/eds/close_once_test.go b/share/eds/close_once_test.go new file mode 100644 index 0000000000..e34299f805 --- /dev/null +++ b/share/eds/close_once_test.go @@ -0,0 +1,89 @@ +package eds + +import ( + "context" + "io" + "testing" + "testing/iotest" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestWithClosedOnce(t *testing.T) { + ctx := context.Background() + stub := &stubEdsAccessorCloser{} + closedOnce := WithClosedOnce(stub) + + _, err := closedOnce.Sample(ctx, shwap.SampleCoords{}) + require.NoError(t, err) + _, err = closedOnce.AxisHalf(ctx, rsmt2d.Row, 0) + require.NoError(t, err) + _, err = closedOnce.RowNamespaceData(ctx, libshare.Namespace{}, 0) + require.NoError(t, err) + _, err = closedOnce.Shares(ctx) + require.NoError(t, err) + + require.NoError(t, closedOnce.Close()) + require.True(t, stub.closed) + + // Ensure that the underlying file is not accessible after closing + _, err = closedOnce.Sample(ctx, shwap.SampleCoords{}) + require.ErrorIs(t, err, errAccessorClosed) + _, err = closedOnce.AxisHalf(ctx, rsmt2d.Row, 0) + require.ErrorIs(t, err, errAccessorClosed) + _, err = closedOnce.RowNamespaceData(ctx, libshare.Namespace{}, 0) + require.ErrorIs(t, err, errAccessorClosed) + _, err = closedOnce.Shares(ctx) + require.ErrorIs(t, err, errAccessorClosed) +} + +type stubEdsAccessorCloser struct { + closed bool +} + +func (s *stubEdsAccessorCloser) Size(context.Context) int { + return 0 +} + +func (s *stubEdsAccessorCloser) DataHash(context.Context) (share.DataHash, error) { + return share.DataHash{}, nil +} + +func (s *stubEdsAccessorCloser) AxisRoots(context.Context) (*share.AxisRoots, error) { + return &share.AxisRoots{}, nil +} + +func (s *stubEdsAccessorCloser) Sample(context.Context, shwap.SampleCoords) (shwap.Sample, error) { + return shwap.Sample{}, nil +} + +func (s *stubEdsAccessorCloser) AxisHalf(context.Context, rsmt2d.Axis, int) (AxisHalf, error) { + return AxisHalf{}, nil +} + +func (s *stubEdsAccessorCloser) RowNamespaceData( + context.Context, + libshare.Namespace, + int, +) (shwap.RowNamespaceData, error) { + return shwap.RowNamespaceData{}, nil +} + +func (s *stubEdsAccessorCloser) Shares(context.Context) ([]libshare.Share, error) { + return nil, nil +} + +func (s *stubEdsAccessorCloser) Reader() (io.Reader, error) { + return iotest.ErrReader(nil), nil +} + +func (s *stubEdsAccessorCloser) Close() error { + s.closed = true + return nil +} diff --git a/share/eds/eds.go b/share/eds/eds.go deleted file mode 100644 index 03753bd44c..0000000000 --- a/share/eds/eds.go +++ /dev/null @@ -1,342 +0,0 @@ -package eds - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "math" - - "github.com/ipfs/go-cid" - "github.com/ipld/go-car" - "github.com/ipld/go-car/util" - "github.com/tendermint/tendermint/crypto/merkle" - corebytes "github.com/tendermint/tendermint/libs/bytes" - coretypes "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/tendermint/tendermint/types" - - pkgproof "github.com/celestiaorg/celestia-app/v2/pkg/proof" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" - "github.com/celestiaorg/go-square/shares" - "github.com/celestiaorg/nmt" - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -var ErrEmptySquare = errors.New("share: importing empty data") - -// WriteEDS writes the entire EDS into the given io.Writer as CARv1 file. -// This includes all shares in quadrant order, followed by all inner nodes of the NMT tree. -// Order: [ Carv1Header | Q1 | Q2 | Q3 | Q4 | inner nodes ] -// For more information about the header: https://ipld.io/specs/transport/car/carv1/#header -func WriteEDS(ctx context.Context, eds *rsmt2d.ExtendedDataSquare, w io.Writer) (err error) { - ctx, span := tracer.Start(ctx, "write-eds") - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - // Creates and writes Carv1Header. Roots are the eds Row + Col roots - err = writeHeader(eds, w) - if err != nil { - return fmt.Errorf("share: writing carv1 header: %w", err) - } - // Iterates over shares in quadrant order via eds.GetCell - err = writeQuadrants(eds, w) - if err != nil { - return fmt.Errorf("share: writing shares: %w", err) - } - - // Iterates over proofs and writes them to the CAR - err = writeProofs(ctx, eds, w) - if err != nil { - return fmt.Errorf("share: writing proofs: %w", err) - } - return nil -} - -// writeHeader creates a CarV1 header using the EDS's Row and Column roots as the list of DAG roots. -func writeHeader(eds *rsmt2d.ExtendedDataSquare, w io.Writer) error { - rootCids, err := rootsToCids(eds) - if err != nil { - return fmt.Errorf("getting root cids: %w", err) - } - - return car.WriteHeader(&car.CarHeader{ - Roots: rootCids, - Version: 1, - }, w) -} - -// writeQuadrants reorders the shares to quadrant order and writes them to the CARv1 file. -func writeQuadrants(eds *rsmt2d.ExtendedDataSquare, w io.Writer) error { - hasher := nmt.NewNmtHasher(share.NewSHA256Hasher(), share.NamespaceSize, ipld.NMTIgnoreMaxNamespace) - shares := quadrantOrder(eds) - for _, share := range shares { - leaf, err := hasher.HashLeaf(share) - if err != nil { - return fmt.Errorf("hashing share: %w", err) - } - cid, err := ipld.CidFromNamespacedSha256(leaf) - if err != nil { - return fmt.Errorf("getting cid from share: %w", err) - } - err = util.LdWrite(w, cid.Bytes(), share) - if err != nil { - return fmt.Errorf("writing share to file: %w", err) - } - } - return nil -} - -// writeProofs iterates over the in-memory blockstore's keys and writes all inner nodes to the -// CARv1 file. -func writeProofs(ctx context.Context, eds *rsmt2d.ExtendedDataSquare, w io.Writer) error { - // check if proofs are collected by ipld.ProofsAdder in previous reconstructions of eds - proofs, err := getProofs(ctx, eds) - if err != nil { - return fmt.Errorf("recomputing proofs: %w", err) - } - - for id, proof := range proofs { - err := util.LdWrite(w, id.Bytes(), proof) - if err != nil { - return fmt.Errorf("writing proof to the car: %w", err) - } - } - return nil -} - -func getProofs(ctx context.Context, eds *rsmt2d.ExtendedDataSquare) (map[cid.Cid][]byte, error) { - // check if there are proofs collected by ipld.ProofsAdder in previous reconstruction of eds - if adder := ipld.ProofsAdderFromCtx(ctx); adder != nil { - defer adder.Purge() - return adder.Proofs(), nil - } - - // recompute proofs from eds - shares := eds.Flattened() - shareCount := len(shares) - if shareCount == 0 { - return nil, ErrEmptySquare - } - odsWidth := int(math.Sqrt(float64(shareCount)) / 2) - - // this adder ignores leaves, so that they are not added to the store we iterate through in - // writeProofs - adder := ipld.NewProofsAdder(odsWidth * 2) - defer adder.Purge() - - eds, err := rsmt2d.ImportExtendedDataSquare( - shares, - share.DefaultRSMT2DCodec(), - wrapper.NewConstructor(uint64(odsWidth), - nmt.NodeVisitor(adder.VisitFn())), - ) - if err != nil { - return nil, fmt.Errorf("recomputing data square: %w", err) - } - // compute roots - if _, err = eds.RowRoots(); err != nil { - return nil, fmt.Errorf("computing row roots: %w", err) - } - - return adder.Proofs(), nil -} - -// quadrantOrder reorders the shares in the EDS to quadrant row-by-row order, prepending the -// respective namespace to the shares. -// e.g. [ Q1 R1 | Q1 R2 | Q1 R3 | Q1 R4 | Q2 R1 | Q2 R2 .... ] -func quadrantOrder(eds *rsmt2d.ExtendedDataSquare) [][]byte { - size := eds.Width() * eds.Width() - shares := make([][]byte, size) - - quadrantWidth := int(eds.Width() / 2) - quadrantSize := quadrantWidth * quadrantWidth - for i := 0; i < quadrantWidth; i++ { - for j := 0; j < quadrantWidth; j++ { - cells := getQuadrantCells(eds, uint(i), uint(j)) - innerOffset := i*quadrantWidth + j - for quadrant := 0; quadrant < 4; quadrant++ { - shares[(quadrant*quadrantSize)+innerOffset] = prependNamespace(quadrant, cells[quadrant]) - } - } - } - return shares -} - -// getQuadrantCells returns the cell of each EDS quadrant with the passed inner-quadrant coordinates -func getQuadrantCells(eds *rsmt2d.ExtendedDataSquare, i, j uint) [][]byte { - cells := make([][]byte, 4) - quadrantWidth := eds.Width() / 2 - cells[0] = eds.GetCell(i, j) - cells[1] = eds.GetCell(i, j+quadrantWidth) - cells[2] = eds.GetCell(i+quadrantWidth, j) - cells[3] = eds.GetCell(i+quadrantWidth, j+quadrantWidth) - return cells -} - -// prependNamespace adds the namespace to the passed share if in the first quadrant, -// otherwise it adds the ParitySharesNamespace to the beginning. -func prependNamespace(quadrant int, shr share.Share) []byte { - namespacedShare := make([]byte, 0, share.NamespaceSize+share.Size) - switch quadrant { - case 0: - return append(append(namespacedShare, share.GetNamespace(shr)...), shr...) - case 1, 2, 3: - return append(append(namespacedShare, share.ParitySharesNamespace...), shr...) - default: - panic("invalid quadrant") - } -} - -// rootsToCids converts the EDS's Row and Column roots to CIDs. -func rootsToCids(eds *rsmt2d.ExtendedDataSquare) ([]cid.Cid, error) { - rowRoots, err := eds.RowRoots() - if err != nil { - return nil, err - } - colRoots, err := eds.ColRoots() - if err != nil { - return nil, err - } - - roots := make([][]byte, 0, len(rowRoots)+len(colRoots)) - roots = append(roots, rowRoots...) - roots = append(roots, colRoots...) - rootCids := make([]cid.Cid, len(roots)) - for i, r := range roots { - rootCids[i], err = ipld.CidFromNamespacedSha256(r) - if err != nil { - return nil, fmt.Errorf("getting cid from root: %w", err) - } - } - return rootCids, nil -} - -// ReadEDS reads the first EDS quadrant (1/4) from an io.Reader CAR file. -// Only the first quadrant will be read, which represents the original data. -// The returned EDS is guaranteed to be full and valid against the DataRoot, otherwise ReadEDS -// errors. -func ReadEDS(ctx context.Context, r io.Reader, root share.DataHash) (eds *rsmt2d.ExtendedDataSquare, err error) { - _, span := tracer.Start(ctx, "read-eds") - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - carReader, err := car.NewCarReader(r) - if err != nil { - return nil, fmt.Errorf("share: reading car file: %w", err) - } - - // car header includes both row and col roots in header - odsWidth := len(carReader.Header.Roots) / 4 - odsSquareSize := odsWidth * odsWidth - shares := make([][]byte, odsSquareSize) - // the first quadrant is stored directly after the header, - // so we can just read the first odsSquareSize blocks - for i := 0; i < odsSquareSize; i++ { - block, err := carReader.Next() - if err != nil { - return nil, fmt.Errorf("share: reading next car entry: %w", err) - } - // the stored first quadrant shares are wrapped with the namespace twice. - // we cut it off here, because it is added again while importing to the tree below - shares[i] = share.GetData(block.RawData()) - } - - // use proofs adder if provided, to cache collected proofs while recomputing the eds - var opts []nmt.Option - visitor := ipld.ProofsAdderFromCtx(ctx).VisitFn() - if visitor != nil { - opts = append(opts, nmt.NodeVisitor(visitor)) - } - - eds, err = rsmt2d.ComputeExtendedDataSquare( - shares, - share.DefaultRSMT2DCodec(), - wrapper.NewConstructor(uint64(odsWidth), opts...), - ) - if err != nil { - return nil, fmt.Errorf("share: computing eds: %w", err) - } - - newDah, err := share.NewRoot(eds) - if err != nil { - return nil, err - } - if !bytes.Equal(newDah.Hash(), root) { - return nil, fmt.Errorf( - "share: content integrity mismatch: imported root %s doesn't match expected root %s", - newDah.Hash(), - root, - ) - } - return eds, nil -} - -// ProveShares generates a share proof for a share range. -// The share range, defined by start and end, is end-exclusive. -func ProveShares(eds *rsmt2d.ExtendedDataSquare, start, end int) (*types.ShareProof, error) { - log.Debugw("proving share range", "start", start, "end", end) - - odsShares, err := shares.FromBytes(eds.FlattenedODS()) - if err != nil { - return nil, err - } - nID, err := pkgproof.ParseNamespace(odsShares, start, end) - if err != nil { - return nil, err - } - log.Debugw("generating the share proof", "start", start, "end", end) - proof, err := pkgproof.NewShareInclusionProofFromEDS(eds, nID, shares.NewRange(start, end)) - if err != nil { - return nil, err - } - coreProof := toCoreShareProof(proof) - return &coreProof, nil -} - -// toCoreShareProof utility function that converts a share proof defined in app -// to the share proof defined in node. -// This will be removed once we unify both these proofs. -// Reference issue: https://github.com/celestiaorg/celestia-app/issues/3734 -func toCoreShareProof(appShareProof pkgproof.ShareProof) types.ShareProof { - shareProofs := make([]*coretypes.NMTProof, 0) - for _, proof := range appShareProof.ShareProofs { - shareProofs = append(shareProofs, &coretypes.NMTProof{ - Start: proof.Start, - End: proof.End, - Nodes: proof.Nodes, - LeafHash: proof.LeafHash, - }) - } - - rowRoots := make([]corebytes.HexBytes, 0) - rowProofs := make([]*merkle.Proof, 0) - for index, proof := range appShareProof.RowProof.Proofs { - rowRoots = append(rowRoots, appShareProof.RowProof.RowRoots[index]) - rowProofs = append(rowProofs, &merkle.Proof{ - Total: proof.Total, - Index: proof.Index, - LeafHash: proof.LeafHash, - Aunts: proof.Aunts, - }) - } - - return types.ShareProof{ - Data: appShareProof.Data, - ShareProofs: shareProofs, - NamespaceID: appShareProof.NamespaceId, - RowProof: types.RowProof{ - RowRoots: rowRoots, - Proofs: rowProofs, - StartRow: appShareProof.RowProof.StartRow, - EndRow: appShareProof.RowProof.EndRow, - }, - NamespaceVersion: appShareProof.NamespaceVersion, - } -} diff --git a/share/eds/eds_test.go b/share/eds/eds_test.go deleted file mode 100644 index fb1c13eaf1..0000000000 --- a/share/eds/eds_test.go +++ /dev/null @@ -1,350 +0,0 @@ -package eds - -import ( - "bytes" - "context" - "embed" - "encoding/json" - "fmt" - "os" - "testing" - - bstore "github.com/ipfs/boxo/blockstore" - ds "github.com/ipfs/go-datastore" - carv1 "github.com/ipld/go-car" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/rand" - coretypes "github.com/tendermint/tendermint/types" - - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - pkgproof "github.com/celestiaorg/celestia-app/v2/pkg/proof" - "github.com/celestiaorg/go-square/namespace" - "github.com/celestiaorg/go-square/shares" - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds/edstest" -) - -//go:embed "testdata/example-root.json" -var exampleRoot string - -//go:embed "testdata/example.car" -var f embed.FS - -func TestQuadrantOrder(t *testing.T) { - testCases := []struct { - name string - squareSize int - }{ - {"smol", 2}, - {"still smol", 8}, - {"default mainnet", appconsts.DefaultGovMaxSquareSize}, - {"max", share.MaxSquareSize}, - } - - testShareSize := 64 - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - shares := make([][]byte, tc.squareSize*tc.squareSize) - - for i := 0; i < tc.squareSize*tc.squareSize; i++ { - shares[i] = rand.Bytes(testShareSize) - } - - eds, err := rsmt2d.ComputeExtendedDataSquare(shares, share.DefaultRSMT2DCodec(), rsmt2d.NewDefaultTree) - require.NoError(t, err) - - res := quadrantOrder(eds) - for _, s := range res { - require.Len(t, s, testShareSize+share.NamespaceSize) - } - - for q := 0; q < 4; q++ { - for i := 0; i < tc.squareSize; i++ { - for j := 0; j < tc.squareSize; j++ { - resIndex := q*tc.squareSize*tc.squareSize + i*tc.squareSize + j - edsRow := q/2*tc.squareSize + i - edsCol := (q%2)*tc.squareSize + j - - assert.Equal(t, res[resIndex], prependNamespace(q, eds.Row(uint(edsRow))[edsCol])) - } - } - } - }) - } -} - -func TestWriteEDS(t *testing.T) { - writeRandomEDS(t) -} - -func TestWriteEDSHeaderRoots(t *testing.T) { - eds := writeRandomEDS(t) - f := openWrittenEDS(t) - defer f.Close() - - reader, err := carv1.NewCarReader(f) - require.NoError(t, err, "error creating car reader") - roots, err := rootsToCids(eds) - require.NoError(t, err, "error converting roots to cids") - require.Equal(t, roots, reader.Header.Roots) -} - -func TestWriteEDSStartsWithLeaves(t *testing.T) { - eds := writeRandomEDS(t) - f := openWrittenEDS(t) - defer f.Close() - - reader, err := carv1.NewCarReader(f) - require.NoError(t, err, "error creating car reader") - block, err := reader.Next() - require.NoError(t, err, "error getting first block") - - require.Equal(t, share.GetData(block.RawData()), eds.GetCell(0, 0)) -} - -func TestWriteEDSIncludesRoots(t *testing.T) { - writeRandomEDS(t) - f := openWrittenEDS(t) - defer f.Close() - - bs := bstore.NewBlockstore(ds.NewMapDatastore()) - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - loaded, err := carv1.LoadCar(ctx, bs, f) - require.NoError(t, err, "error loading car file") - for _, root := range loaded.Roots { - ok, err := bs.Has(context.Background(), root) - require.NoError(t, err, "error checking if blockstore has root") - require.True(t, ok, "blockstore does not have root") - } -} - -func TestWriteEDSInQuadrantOrder(t *testing.T) { - eds := writeRandomEDS(t) - f := openWrittenEDS(t) - defer f.Close() - - reader, err := carv1.NewCarReader(f) - require.NoError(t, err, "error creating car reader") - - shares := quadrantOrder(eds) - for i := 0; i < len(shares); i++ { - block, err := reader.Next() - require.NoError(t, err, "error getting block") - require.Equal(t, block.RawData(), shares[i]) - } -} - -func TestReadWriteRoundtrip(t *testing.T) { - eds := writeRandomEDS(t) - dah, err := share.NewRoot(eds) - require.NoError(t, err) - f := openWrittenEDS(t) - defer f.Close() - - loaded, err := ReadEDS(context.Background(), f, dah.Hash()) - require.NoError(t, err, "error reading EDS from file") - - rowRoots, err := eds.RowRoots() - require.NoError(t, err) - loadedRowRoots, err := loaded.RowRoots() - require.NoError(t, err) - require.Equal(t, rowRoots, loadedRowRoots) - - colRoots, err := eds.ColRoots() - require.NoError(t, err) - loadedColRoots, err := loaded.ColRoots() - require.NoError(t, err) - require.Equal(t, colRoots, loadedColRoots) -} - -func TestReadEDS(t *testing.T) { - f, err := f.Open("testdata/example.car") - require.NoError(t, err, "error opening file") - - var dah da.DataAvailabilityHeader - err = json.Unmarshal([]byte(exampleRoot), &dah) - require.NoError(t, err, "error unmarshaling example root") - - loaded, err := ReadEDS(context.Background(), f, dah.Hash()) - require.NoError(t, err, "error reading EDS from file") - rowRoots, err := loaded.RowRoots() - require.NoError(t, err) - require.Equal(t, dah.RowRoots, rowRoots) - colRoots, err := loaded.ColRoots() - require.NoError(t, err) - require.Equal(t, dah.ColumnRoots, colRoots) -} - -func TestReadEDSContentIntegrityMismatch(t *testing.T) { - writeRandomEDS(t) - dah, err := da.NewDataAvailabilityHeader(edstest.RandEDS(t, 4)) - require.NoError(t, err) - f := openWrittenEDS(t) - defer f.Close() - - _, err = ReadEDS(context.Background(), f, dah.Hash()) - require.ErrorContains(t, err, "share: content integrity mismatch: imported root") -} - -// BenchmarkReadWriteEDS benchmarks the time it takes to write and read an EDS from disk. The -// benchmark is run with a 4x4 ODS to a 64x64 ODS - a higher value can be used, but it will run for -// much longer. -func BenchmarkReadWriteEDS(b *testing.B) { - ctx, cancel := context.WithCancel(context.Background()) - b.Cleanup(cancel) - for originalDataWidth := 4; originalDataWidth <= 64; originalDataWidth *= 2 { - eds := edstest.RandEDS(b, originalDataWidth) - dah, err := share.NewRoot(eds) - require.NoError(b, err) - b.Run(fmt.Sprintf("Writing %dx%d", originalDataWidth, originalDataWidth), func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - f := new(bytes.Buffer) - err := WriteEDS(ctx, eds, f) - require.NoError(b, err) - } - }) - b.Run(fmt.Sprintf("Reading %dx%d", originalDataWidth, originalDataWidth), func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - b.StopTimer() - f := new(bytes.Buffer) - _ = WriteEDS(ctx, eds, f) - b.StartTimer() - _, err := ReadEDS(ctx, f, dah.Hash()) - require.NoError(b, err) - } - }) - } -} - -func writeRandomEDS(t *testing.T) *rsmt2d.ExtendedDataSquare { - t.Helper() - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - tmpDir := t.TempDir() - err := os.Chdir(tmpDir) - require.NoError(t, err, "error changing to the temporary test directory") - f, err := os.OpenFile("test.car", os.O_WRONLY|os.O_CREATE, 0o600) - require.NoError(t, err, "error opening file") - - eds := edstest.RandEDS(t, 4) - err = WriteEDS(ctx, eds, f) - require.NoError(t, err, "error writing EDS to file") - f.Close() - return eds -} - -func openWrittenEDS(t *testing.T) *os.File { - t.Helper() - f, err := os.OpenFile("test.car", os.O_RDONLY, 0o600) - require.NoError(t, err, "error opening file") - return f -} - -/* -use this function as needed to create new test data. - -example: - - func Test_CreateData(t *testing.T) { - createTestData(t, "celestia-node/share/eds/testdata") - } -*/ -func createTestData(t *testing.T, testDir string) { //nolint:unused - t.Helper() - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - err := os.Chdir(testDir) - require.NoError(t, err, "changing to the directory") - os.RemoveAll("example.car") - require.NoError(t, err, "removing old file") - f, err := os.OpenFile("example.car", os.O_WRONLY|os.O_CREATE, 0o600) - require.NoError(t, err, "opening file") - - eds := edstest.RandEDS(t, 4) - err = WriteEDS(ctx, eds, f) - require.NoError(t, err, "writing EDS to file") - f.Close() - dah, err := share.NewRoot(eds) - require.NoError(t, err) - - header, err := json.MarshalIndent(dah, "", "") - require.NoError(t, err, "marshaling example root") - os.RemoveAll("example-root.json") - require.NoError(t, err, "removing old file") - f, err = os.OpenFile("example-root.json", os.O_WRONLY|os.O_CREATE, 0o600) - require.NoError(t, err, "opening file") - _, err = f.Write(header) - require.NoError(t, err, "writing example root to file") - f.Close() -} - -func TestProveShares(t *testing.T) { - ns := namespace.RandomBlobNamespace() - eds, dataRoot := edstest.RandEDSWithNamespace( - t, - ns.Bytes(), - 16, - ) - - tests := map[string]struct { - start, end int - expectedProof coretypes.ShareProof - expectErr bool - }{ - "start share == end share": { - start: 2, - end: 2, - expectErr: true, - }, - "start share > end share": { - start: 3, - end: 2, - expectErr: true, - }, - "start share > number of shares in the block": { - start: 2000, - end: 2010, - expectErr: true, - }, - "end share > number of shares in the block": { - start: 1, - end: 2010, - expectErr: true, - }, - "valid case": { - start: 0, - end: 2, - expectedProof: func() coretypes.ShareProof { - proof, err := pkgproof.NewShareInclusionProofFromEDS( - eds, - ns, - shares.NewRange(0, 2), - ) - require.NoError(t, err) - require.NoError(t, proof.Validate(dataRoot.Hash())) - return toCoreShareProof(proof) - }(), - }, - } - - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - result, err := ProveShares(eds, tc.start, tc.end) - if tc.expectErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tc.expectedProof, *result) - assert.NoError(t, result.Validate(dataRoot.Hash())) - } - }) - } -} diff --git a/share/eds/edstest/testing.go b/share/eds/edstest/testing.go index c5131f8656..a022087978 100644 --- a/share/eds/edstest/testing.go +++ b/share/eds/edstest/testing.go @@ -1,29 +1,27 @@ package edstest import ( + "crypto/rand" "testing" "github.com/stretchr/testify/require" coretypes "github.com/tendermint/tendermint/types" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/celestia-app/v2/pkg/user" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" - "github.com/celestiaorg/celestia-app/v2/test/util/blobfactory" - "github.com/celestiaorg/celestia-app/v2/test/util/testfactory" - "github.com/celestiaorg/celestia-app/v2/x/blob/types" - "github.com/celestiaorg/go-square/blob" - "github.com/celestiaorg/go-square/namespace" - "github.com/celestiaorg/go-square/shares" - "github.com/celestiaorg/go-square/square" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/user" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + "github.com/celestiaorg/celestia-app/v3/test/util/blobfactory" + "github.com/celestiaorg/celestia-app/v3/test/util/testfactory" + blobtypes "github.com/celestiaorg/celestia-app/v3/x/blob/types" + libSquare "github.com/celestiaorg/go-square/v2" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/sharetest" ) const ( @@ -31,37 +29,86 @@ const ( testChainID = "private" ) -func RandByzantineEDS(t testing.TB, size int, options ...nmt.Option) *rsmt2d.ExtendedDataSquare { - eds := RandEDS(t, size) +func RandByzantineEDS(t testing.TB, odsSize int, options ...nmt.Option) *rsmt2d.ExtendedDataSquare { + eds := RandEDS(t, odsSize) shares := eds.Flattened() - copy(share.GetData(shares[0]), share.GetData(shares[1])) // corrupting eds - eds, err := rsmt2d.ImportExtendedDataSquare(shares, + copy(shares[0][libshare.NamespaceSize:], shares[1][libshare.NamespaceSize:]) // corrupting eds + eds, err := rsmt2d.ImportExtendedDataSquare( + shares, share.DefaultRSMT2DCodec(), - wrapper.NewConstructor(uint64(size), - options...)) + wrapper.NewConstructor(uint64(odsSize), options...), + ) require.NoError(t, err, "failure to recompute the extended data square") return eds } // RandEDS generates EDS filled with the random data with the given size for original square. -func RandEDS(t testing.TB, size int) *rsmt2d.ExtendedDataSquare { - shares := sharetest.RandShares(t, size*size) - eds, err := rsmt2d.ComputeExtendedDataSquare(shares, share.DefaultRSMT2DCodec(), wrapper.NewConstructor(uint64(size))) +func RandEDS(t testing.TB, odsSize int) *rsmt2d.ExtendedDataSquare { + shares, err := libshare.RandShares(odsSize * odsSize) + require.NoError(t, err) + eds, err := rsmt2d.ComputeExtendedDataSquare( + libshare.ToBytes(shares), + share.DefaultRSMT2DCodec(), + wrapper.NewConstructor(uint64(odsSize)), + ) + require.NoError(t, err, "failure to recompute the extended data square") + return eds +} + +// RandEDSWithTailPadding generates EDS of given ODS size filled with randomized and tail padding shares. +func RandEDSWithTailPadding(t testing.TB, odsSize, padding int) *rsmt2d.ExtendedDataSquare { + shares, err := libshare.RandShares(odsSize * odsSize) + require.NoError(t, err) + for i := len(shares) - padding; i < len(shares); i++ { + paddingShare := libshare.TailPaddingShare() + shares[i] = paddingShare + } + + eds, err := rsmt2d.ComputeExtendedDataSquare( + libshare.ToBytes(shares), + share.DefaultRSMT2DCodec(), + wrapper.NewConstructor(uint64(odsSize)), + ) require.NoError(t, err, "failure to recompute the extended data square") return eds } +// RandEDSWithNamespace generates EDS with given square size. Returned EDS will have +// namespacedAmount of shares with the given namespace. func RandEDSWithNamespace( t testing.TB, - namespace share.Namespace, - size int, -) (*rsmt2d.ExtendedDataSquare, *share.Root) { - shares := sharetest.RandSharesWithNamespace(t, namespace, size*size) - eds, err := rsmt2d.ComputeExtendedDataSquare(shares, share.DefaultRSMT2DCodec(), wrapper.NewConstructor(uint64(size))) + namespace libshare.Namespace, + namespacedAmount, odsSize int, +) (*rsmt2d.ExtendedDataSquare, *share.AxisRoots) { + shares, err := libshare.RandSharesWithNamespace(namespace, namespacedAmount, odsSize*odsSize) + require.NoError(t, err) + eds, err := rsmt2d.ComputeExtendedDataSquare( + libshare.ToBytes(shares), + share.DefaultRSMT2DCodec(), + wrapper.NewConstructor(uint64(odsSize)), + ) require.NoError(t, err, "failure to recompute the extended data square") - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - return eds, dah + return eds, roots +} + +// RandomAxisRoots generates random share.AxisRoots for the given eds size. +func RandomAxisRoots(t testing.TB, edsSize int) *share.AxisRoots { + roots := make([][]byte, edsSize*2) + for i := range roots { + root := make([]byte, edsSize) + _, err := rand.Read(root) + require.NoError(t, err) + roots[i] = root + } + + rows := roots[:edsSize] + cols := roots[edsSize:] + return &share.AxisRoots{ + RowRoots: rows, + ColumnRoots: cols, + } } // GenerateTestBlock generates a set of test blocks with a specific blob size and number of @@ -70,9 +117,9 @@ func GenerateTestBlock( t *testing.T, blobSize, numberOfTransactions int, ) ( - []*types.MsgPayForBlobs, - []*blob.Blob, - []namespace.Namespace, + []*blobtypes.MsgPayForBlobs, + []*libshare.Blob, + []libshare.Namespace, *rsmt2d.ExtendedDataSquare, coretypes.Txs, *da.DataAvailabilityHeader, @@ -86,7 +133,7 @@ func GenerateTestBlock( txs := make(coretypes.Txs, 0) txs = append(txs, coreTxs...) - dataSquare, err := square.Construct( + square, err := libSquare.Construct( txs.ToSliceOfBytes(), appconsts.SquareSizeUpperBound(appconsts.LatestVersion), appconsts.SubtreeRootThreshold(appconsts.LatestVersion), @@ -94,7 +141,7 @@ func GenerateTestBlock( require.NoError(t, err) // erasure the data square which we use to create the data root. - eds, err := da.ExtendShares(shares.ToBytes(dataSquare)) + eds, err := da.ExtendShares(libshare.ToBytes(square)) require.NoError(t, err) // create the new data root by creating the data availability header (merkle @@ -112,10 +159,10 @@ func GenerateTestBlock( func createTestBlobTransactions( t *testing.T, numberOfTransactions, size int, -) ([]namespace.Namespace, []*types.MsgPayForBlobs, []*blob.Blob, []coretypes.Tx) { - nss := make([]namespace.Namespace, 0) - msgs := make([]*types.MsgPayForBlobs, 0) - blobs := make([]*blob.Blob, 0) +) ([]libshare.Namespace, []*blobtypes.MsgPayForBlobs, []*libshare.Blob, []coretypes.Tx) { + nss := make([]libshare.Namespace, 0) + msgs := make([]*blobtypes.MsgPayForBlobs, 0) + blobs := make([]*libshare.Blob, 0) coreTxs := make([]coretypes.Tx, 0) config := encoding.MakeConfig(app.ModuleEncodingRegisters...) keyring := testfactory.TestKeyring(config.Codec, accountName) @@ -140,11 +187,11 @@ func createTestBlobTransaction( t *testing.T, signer *user.Signer, size int, -) (namespace.Namespace, *types.MsgPayForBlobs, *blob.Blob, coretypes.Tx) { - ns := namespace.RandomBlobNamespace() +) (libshare.Namespace, *blobtypes.MsgPayForBlobs, *libshare.Blob, coretypes.Tx) { + ns := libshare.RandomBlobNamespace() account := signer.Account(accountName) msg, b := blobfactory.RandMsgPayForBlobsWithNamespaceAndSigner(account.Address().String(), ns, size) - cTx, _, err := signer.CreatePayForBlobs(accountName, []*blob.Blob{b}) + cTx, _, err := signer.CreatePayForBlobs(accountName, []*libshare.Blob{b}) require.NoError(t, err) return ns, msg, b, cTx } diff --git a/share/eds/inverted_index.go b/share/eds/inverted_index.go deleted file mode 100644 index 799ab6208d..0000000000 --- a/share/eds/inverted_index.go +++ /dev/null @@ -1,102 +0,0 @@ -package eds - -import ( - "context" - "errors" - "fmt" - "runtime" - - "github.com/dgraph-io/badger/v4/options" - "github.com/filecoin-project/dagstore/index" - "github.com/filecoin-project/dagstore/shard" - ds "github.com/ipfs/go-datastore" - dsbadger "github.com/ipfs/go-ds-badger4" - "github.com/multiformats/go-multihash" -) - -const invertedIndexPath = "/inverted_index/" - -// ErrNotFoundInIndex is returned instead of ErrNotFound if the multihash doesn't exist in the index -var ErrNotFoundInIndex = errors.New("does not exist in index") - -// simpleInvertedIndex is an inverted index that only stores a single shard key per multihash. Its -// implementation is modified from the default upstream implementation in dagstore/index. -type simpleInvertedIndex struct { - ds ds.Batching -} - -// newSimpleInvertedIndex returns a new inverted index that only stores a single shard key per -// multihash. This is because we use badger as a storage backend, so updates are expensive, and we -// don't care which shard is used to serve a cid. -func newSimpleInvertedIndex(storePath string) (*simpleInvertedIndex, error) { - opts := dsbadger.DefaultOptions // this should be copied - // turn off value log GC as we don't use value log - opts.GcInterval = 0 - // use minimum amount of NumLevelZeroTables to trigger L0 compaction faster - opts.NumLevelZeroTables = 1 - // MaxLevels = 8 will allow the db to grow to ~11.1 TiB - opts.MaxLevels = 8 - // inverted index stores unique hash keys, so we don't need to detect conflicts - opts.DetectConflicts = false - // we don't need compression for inverted index as it just hashes - opts.Compression = options.None - compactors := runtime.NumCPU() - if compactors < 2 { - compactors = 2 - } - if compactors > opts.MaxLevels { // ensure there is no more compactors than db table levels - compactors = opts.MaxLevels - } - opts.NumCompactors = compactors - - ds, err := dsbadger.NewDatastore(storePath+invertedIndexPath, &opts) - if err != nil { - return nil, fmt.Errorf("can't open Badger Datastore: %w", err) - } - - return &simpleInvertedIndex{ds: ds}, nil -} - -func (s *simpleInvertedIndex) AddMultihashesForShard( - ctx context.Context, - mhIter index.MultihashIterator, - sk shard.Key, -) error { - // in the original implementation, a mutex is used here to prevent unnecessary updates to the - // key. The amount of extra data produced by this is negligible, and the performance benefits - // from removing the lock are significant (indexing is a hot path during sync). - batch, err := s.ds.Batch(ctx) - if err != nil { - return fmt.Errorf("failed to create ds batch: %w", err) - } - - err = mhIter.ForEach(func(mh multihash.Multihash) error { - key := ds.NewKey(string(mh)) - if err := batch.Put(ctx, key, []byte(sk.String())); err != nil { - return fmt.Errorf("failed to put mh=%s, err=%w", mh, err) - } - return nil - }) - if err != nil { - return fmt.Errorf("failed to add index entry: %w", err) - } - - if err := batch.Commit(ctx); err != nil { - return fmt.Errorf("failed to commit batch: %w", err) - } - return nil -} - -func (s *simpleInvertedIndex) GetShardsForMultihash(ctx context.Context, mh multihash.Multihash) ([]shard.Key, error) { - key := ds.NewKey(string(mh)) - sbz, err := s.ds.Get(ctx, key) - if err != nil { - return nil, errors.Join(ErrNotFoundInIndex, err) - } - - return []shard.Key{shard.KeyFromString(string(sbz))}, nil -} - -func (s *simpleInvertedIndex) close() error { - return s.ds.Close() -} diff --git a/share/eds/inverted_index_test.go b/share/eds/inverted_index_test.go deleted file mode 100644 index e83c2be267..0000000000 --- a/share/eds/inverted_index_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package eds - -import ( - "context" - "testing" - - "github.com/filecoin-project/dagstore/shard" - "github.com/multiformats/go-multihash" - "github.com/stretchr/testify/require" -) - -type mockIterator struct { - mhs []multihash.Multihash -} - -func (m *mockIterator) ForEach(f func(mh multihash.Multihash) error) error { - for _, mh := range m.mhs { - if err := f(mh); err != nil { - return err - } - } - return nil -} - -// TestMultihashesForShard ensures that the inverted index correctly stores a single shard key per -// duplicate multihash -func TestMultihashesForShard(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - mhs := []multihash.Multihash{ - multihash.Multihash("mh1"), - multihash.Multihash("mh2"), - multihash.Multihash("mh3"), - } - - mi := &mockIterator{mhs: mhs} - path := t.TempDir() - invertedIndex, err := newSimpleInvertedIndex(path) - require.NoError(t, err) - - // 1. Add all 3 multihashes to shard1 - err = invertedIndex.AddMultihashesForShard(ctx, mi, shard.KeyFromString("shard1")) - require.NoError(t, err) - shardKeys, err := invertedIndex.GetShardsForMultihash(ctx, mhs[0]) - require.NoError(t, err) - require.Equal(t, []shard.Key{shard.KeyFromString("shard1")}, shardKeys) - - // 2. Add mh1 to shard2, and ensure that mh1 no longer points to shard1 - err = invertedIndex.AddMultihashesForShard(ctx, &mockIterator{mhs: mhs[:1]}, shard.KeyFromString("shard2")) - require.NoError(t, err) - shardKeys, err = invertedIndex.GetShardsForMultihash(ctx, mhs[0]) - require.NoError(t, err) - require.Equal(t, []shard.Key{shard.KeyFromString("shard2")}, shardKeys) -} diff --git a/share/eds/metrics.go b/share/eds/metrics.go deleted file mode 100644 index 1ce9fe459d..0000000000 --- a/share/eds/metrics.go +++ /dev/null @@ -1,286 +0,0 @@ -package eds - -import ( - "context" - "errors" - "time" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric" - - "github.com/celestiaorg/celestia-node/libs/utils" -) - -const ( - failedKey = "failed" - sizeKey = "eds_size" - - putResultKey = "result" - putOK putResult = "ok" - putExists putResult = "exists" - putFailed putResult = "failed" - - opNameKey = "op" - longOpResultKey = "result" - longOpUnresolved longOpResult = "unresolved" - longOpOK longOpResult = "ok" - longOpFailed longOpResult = "failed" - - dagstoreShardStatusKey = "shard_status" -) - -var meter = otel.Meter("eds_store") - -type putResult string - -type longOpResult string - -type metrics struct { - putTime metric.Float64Histogram - getCARTime metric.Float64Histogram - getCARBlockstoreTime metric.Float64Histogram - getDAHTime metric.Float64Histogram - removeTime metric.Float64Histogram - getTime metric.Float64Histogram - hasTime metric.Float64Histogram - listTime metric.Float64Histogram - - shardFailureCount metric.Int64Counter - - longOpTime metric.Float64Histogram - gcTime metric.Float64Histogram - - clientReg metric.Registration - closerFn func() error -} - -func (s *Store) WithMetrics() error { - putTime, err := meter.Float64Histogram("eds_store_put_time_histogram", - metric.WithDescription("eds store put time histogram(s)")) - if err != nil { - return err - } - - getCARTime, err := meter.Float64Histogram("eds_store_get_car_time_histogram", - metric.WithDescription("eds store get car time histogram(s)")) - if err != nil { - return err - } - - getCARBlockstoreTime, err := meter.Float64Histogram("eds_store_get_car_blockstore_time_histogram", - metric.WithDescription("eds store get car blockstore time histogram(s)")) - if err != nil { - return err - } - - getDAHTime, err := meter.Float64Histogram("eds_store_get_dah_time_histogram", - metric.WithDescription("eds store get dah time histogram(s)")) - if err != nil { - return err - } - - removeTime, err := meter.Float64Histogram("eds_store_remove_time_histogram", - metric.WithDescription("eds store remove time histogram(s)")) - if err != nil { - return err - } - - getTime, err := meter.Float64Histogram("eds_store_get_time_histogram", - metric.WithDescription("eds store get time histogram(s)")) - if err != nil { - return err - } - - hasTime, err := meter.Float64Histogram("eds_store_has_time_histogram", - metric.WithDescription("eds store has time histogram(s)")) - if err != nil { - return err - } - - listTime, err := meter.Float64Histogram("eds_store_list_time_histogram", - metric.WithDescription("eds store list time histogram(s)")) - if err != nil { - return err - } - - shardFailureCount, err := meter.Int64Counter("eds_store_shard_failure_counter", - metric.WithDescription("eds store OpShardFail counter")) - if err != nil { - return err - } - - longOpTime, err := meter.Float64Histogram("eds_store_long_operation_time_histogram", - metric.WithDescription("eds store long operation time histogram(s)")) - if err != nil { - return err - } - - gcTime, err := meter.Float64Histogram("eds_store_gc_time", - metric.WithDescription("dagstore gc time histogram(s)")) - if err != nil { - return err - } - - dagStoreShards, err := meter.Int64ObservableGauge("eds_store_dagstore_shards", - metric.WithDescription("dagstore amount of shards by status")) - if err != nil { - return err - } - - closerFn, err := s.cache.Load().EnableMetrics() - if err != nil { - return err - } - - callback := func(_ context.Context, observer metric.Observer) error { - stats := s.dgstr.Stats() - for status, amount := range stats { - observer.ObserveInt64(dagStoreShards, int64(amount), - metric.WithAttributes( - attribute.String(dagstoreShardStatusKey, status.String()), - )) - } - return nil - } - - clientReg, err := meter.RegisterCallback(callback, dagStoreShards) - if err != nil { - return err - } - - s.metrics = &metrics{ - putTime: putTime, - getCARTime: getCARTime, - getCARBlockstoreTime: getCARBlockstoreTime, - getDAHTime: getDAHTime, - removeTime: removeTime, - getTime: getTime, - hasTime: hasTime, - listTime: listTime, - shardFailureCount: shardFailureCount, - longOpTime: longOpTime, - gcTime: gcTime, - clientReg: clientReg, - closerFn: closerFn, - } - return nil -} - -func (m *metrics) close() error { - if m == nil { - return nil - } - - return errors.Join(m.closerFn(), m.clientReg.Unregister()) -} - -func (m *metrics) observeGCtime(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - m.gcTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} - -func (m *metrics) observeShardFailure(ctx context.Context, shardKey string) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.shardFailureCount.Add(ctx, 1, metric.WithAttributes(attribute.String("shard_key", shardKey))) -} - -func (m *metrics) observePut(ctx context.Context, dur time.Duration, result putResult, size uint) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.putTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.String(putResultKey, string(result)), - attribute.Int(sizeKey, int(size)))) -} - -func (m *metrics) observeLongOp(ctx context.Context, opName string, dur time.Duration, result longOpResult) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.longOpTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.String(opNameKey, opName), - attribute.String(longOpResultKey, string(result)))) -} - -func (m *metrics) observeGetCAR(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.getCARTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} - -func (m *metrics) observeCARBlockstore(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.getCARBlockstoreTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} - -func (m *metrics) observeGetDAH(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.getDAHTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} - -func (m *metrics) observeRemove(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.removeTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} - -func (m *metrics) observeGet(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.getTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} - -func (m *metrics) observeHas(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.hasTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} - -func (m *metrics) observeList(ctx context.Context, dur time.Duration, failed bool) { - if m == nil { - return - } - ctx = utils.ResetContextOnError(ctx) - - m.listTime.Record(ctx, dur.Seconds(), metric.WithAttributes( - attribute.Bool(failedKey, failed))) -} diff --git a/share/eds/nd.go b/share/eds/nd.go new file mode 100644 index 0000000000..7c41655898 --- /dev/null +++ b/share/eds/nd.go @@ -0,0 +1,38 @@ +package eds + +import ( + "context" + "fmt" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +// NamespaceData extracts shares for a specific namespace from an EDS, considering +// each row independently. It uses root to determine which rows to extract data from, +// avoiding the need to recalculate the row roots for each row. +func NamespaceData( + ctx context.Context, + eds Accessor, + namespace libshare.Namespace, +) (shwap.NamespaceData, error) { + roots, err := eds.AxisRoots(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get AxisRoots: %w", err) + } + rowIdxs, err := share.RowsWithNamespace(roots, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get row indexes: %w", err) + } + rows := make(shwap.NamespaceData, len(rowIdxs)) + for i, idx := range rowIdxs { + rows[i], err = eds.RowNamespaceData(ctx, namespace, idx) + if err != nil { + return nil, fmt.Errorf("failed to process row %d: %w", idx, err) + } + } + + return rows, nil +} diff --git a/share/eds/nd_test.go b/share/eds/nd_test.go new file mode 100644 index 0000000000..457e331219 --- /dev/null +++ b/share/eds/nd_test.go @@ -0,0 +1,33 @@ +package eds + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share/eds/edstest" +) + +func TestNamespaceData(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + + const odsSize = 8 + sharesAmount := odsSize * odsSize + namespace := libshare.RandomNamespace() + for amount := 1; amount < sharesAmount; amount++ { + eds, root := edstest.RandEDSWithNamespace(t, namespace, amount, odsSize) + rsmt2d := &Rsmt2D{ExtendedDataSquare: eds} + nd, err := NamespaceData(ctx, rsmt2d, namespace) + require.NoError(t, err) + require.True(t, len(nd) > 0) + require.Len(t, nd.Flatten(), amount) + + err = nd.Verify(root, namespace) + require.NoError(t, err) + } +} diff --git a/share/eds/ods.go b/share/eds/ods.go deleted file mode 100644 index a924e1c235..0000000000 --- a/share/eds/ods.go +++ /dev/null @@ -1,98 +0,0 @@ -package eds - -import ( - "bufio" - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - - cbor "github.com/ipfs/go-ipld-cbor" - "github.com/ipld/go-car" - "github.com/ipld/go-car/util" -) - -// bufferedODSReader will read odsSquareSize amount of leaves from reader into the buffer. -// It exposes the buffer to be read by io.Reader interface implementation -type bufferedODSReader struct { - carReader *bufio.Reader - // current is the amount of CARv1 encoded leaves that have been read from reader. When current - // reaches odsSquareSize, bufferedODSReader will prevent further reads by returning io.EOF - current, odsSquareSize int - buf *bytes.Buffer -} - -// ODSReader reads CARv1 encoded data from io.ReadCloser and limits the reader to the CAR header -// and first quadrant (ODS) -func ODSReader(carReader io.Reader) (io.Reader, error) { - if carReader == nil { - return nil, errors.New("eds: can't create ODSReader over nil reader") - } - - odsR := &bufferedODSReader{ - carReader: bufio.NewReader(carReader), - buf: new(bytes.Buffer), - } - - // first LdRead reads the full CAR header to determine amount of shares in the ODS - data, err := util.LdRead(odsR.carReader) - if err != nil { - return nil, fmt.Errorf("reading header: %w", err) - } - - var header car.CarHeader - err = cbor.DecodeInto(data, &header) - if err != nil { - return nil, fmt.Errorf("invalid header: %w", err) - } - - // car header contains both row roots and col roots which is why - // we divide by 4 to get the ODSWidth - odsWidth := len(header.Roots) / 4 - odsR.odsSquareSize = odsWidth * odsWidth - - // NewCarReader will expect to read the header first, so write it first - return odsR, util.LdWrite(odsR.buf, data) -} - -func (r *bufferedODSReader) Read(p []byte) (n int, err error) { - // read leafs to the buffer until it has sufficient data to fill provided container or full ods is - // read - for r.current < r.odsSquareSize && r.buf.Len() < len(p) { - if err := r.readLeaf(); err != nil { - return 0, err - } - - r.current++ - } - - // read buffer to slice - return r.buf.Read(p) -} - -// readLeaf reads one leaf from reader into bufferedODSReader buffer -func (r *bufferedODSReader) readLeaf() error { - if _, err := r.carReader.Peek(1); err != nil { // no more blocks, likely clean io.EOF - return err - } - - l, err := binary.ReadUvarint(r.carReader) - if err != nil { - if errors.Is(err, io.EOF) { - return io.ErrUnexpectedEOF // don't silently pretend this is a clean EOF - } - return err - } - - if l > uint64(util.MaxAllowedSectionSize) { // Don't OOM - return fmt.Errorf("malformed car; header `length`: %v is bigger than %v", l, util.MaxAllowedSectionSize) - } - - buf := make([]byte, 8) - n := binary.PutUvarint(buf, l) - r.buf.Write(buf[:n]) - - _, err = r.buf.ReadFrom(io.LimitReader(r.carReader, int64(l))) - return err -} diff --git a/share/eds/ods_test.go b/share/eds/ods_test.go deleted file mode 100644 index 0f7c69e708..0000000000 --- a/share/eds/ods_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package eds - -import ( - "context" - "io" - "testing" - - "github.com/ipld/go-car" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-node/share" -) - -// TestODSReader ensures that the reader returned from ODSReader is capable of reading the CAR -// header and ODS. -func TestODSReader(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - // launch eds store - edsStore, err := newStore(t) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - - // generate random eds data and put it into the store - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // get CAR reader from store - r, err := edsStore.GetCAR(ctx, dah.Hash()) - assert.NoError(t, err) - defer func() { - require.NoError(t, r.Close()) - }() - - // create ODSReader wrapper based on car reader to limit reads to ODS only - odsR, err := ODSReader(r) - assert.NoError(t, err) - - // create CAR reader from ODSReader - carReader, err := car.NewCarReader(odsR) - assert.NoError(t, err) - - // validate ODS could be obtained from reader - for i := 0; i < 4; i++ { - for j := 0; j < 4; j++ { - // pick share from original eds - original := eds.GetCell(uint(i), uint(j)) - - // read block from odsReader based reader - block, err := carReader.Next() - assert.NoError(t, err) - - // check that original data from eds is same as data from reader - assert.Equal(t, original, share.GetData(block.RawData())) - } - } - - // Make sure no excess data is available to get from reader - _, err = carReader.Next() - assert.Error(t, io.EOF, err) -} - -// TestODSReaderReconstruction ensures that the reader returned from ODSReader provides sufficient -// data for EDS reconstruction -func TestODSReaderReconstruction(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - // launch eds store - edsStore, err := newStore(t) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - - // generate random eds data and put it into the store - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // get CAR reader from store - r, err := edsStore.GetCAR(ctx, dah.Hash()) - assert.NoError(t, err) - defer func() { - require.NoError(t, r.Close()) - }() - - // create ODSReader wrapper based on car reader to limit reads to ODS only - odsR, err := ODSReader(r) - assert.NoError(t, err) - - // reconstruct EDS from ODSReader - loaded, err := ReadEDS(ctx, odsR, dah.Hash()) - assert.NoError(t, err) - - rowRoots, err := eds.RowRoots() - require.NoError(t, err) - loadedRowRoots, err := loaded.RowRoots() - require.NoError(t, err) - require.Equal(t, rowRoots, loadedRowRoots) - - colRoots, err := eds.ColRoots() - require.NoError(t, err) - loadedColRoots, err := loaded.ColRoots() - require.NoError(t, err) - require.Equal(t, colRoots, loadedColRoots) -} diff --git a/share/eds/proof.go b/share/eds/proof.go new file mode 100644 index 0000000000..f7e0f71136 --- /dev/null +++ b/share/eds/proof.go @@ -0,0 +1,75 @@ +package eds + +import ( + "github.com/tendermint/tendermint/crypto/merkle" + corebytes "github.com/tendermint/tendermint/libs/bytes" + coretypes "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/types" + + pkgproof "github.com/celestiaorg/celestia-app/v3/pkg/proof" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" +) + +// ProveShares generates a share proof for a share range. +// The share range, defined by start and end, is end-exclusive. +func ProveShares(eds *rsmt2d.ExtendedDataSquare, start, end int) (*types.ShareProof, error) { + log.Debugw("proving share range", "start", start, "end", end) + + odsShares, err := libshare.FromBytes(eds.FlattenedODS()) + if err != nil { + return nil, err + } + nID, err := pkgproof.ParseNamespace(odsShares, start, end) + if err != nil { + return nil, err + } + log.Debugw("generating the share proof", "start", start, "end", end) + proof, err := pkgproof.NewShareInclusionProofFromEDS(eds, nID, libshare.NewRange(start, end)) + if err != nil { + return nil, err + } + coreProof := toCoreShareProof(proof) + return &coreProof, nil +} + +// toCoreShareProof utility function that converts a share proof defined in app +// to the share proof defined in node. +// This will be removed once we unify both these proofs. +// Reference issue: https://github.com/celestiaorg/celestia-app/issues/3734 +func toCoreShareProof(appShareProof pkgproof.ShareProof) types.ShareProof { + shareProofs := make([]*coretypes.NMTProof, 0) + for _, proof := range appShareProof.ShareProofs { + shareProofs = append(shareProofs, &coretypes.NMTProof{ + Start: proof.Start, + End: proof.End, + Nodes: proof.Nodes, + LeafHash: proof.LeafHash, + }) + } + + rowRoots := make([]corebytes.HexBytes, 0) + rowProofs := make([]*merkle.Proof, 0) + for index, proof := range appShareProof.RowProof.Proofs { + rowRoots = append(rowRoots, appShareProof.RowProof.RowRoots[index]) + rowProofs = append(rowProofs, &merkle.Proof{ + Total: proof.Total, + Index: proof.Index, + LeafHash: proof.LeafHash, + Aunts: proof.Aunts, + }) + } + + return types.ShareProof{ + Data: appShareProof.Data, + ShareProofs: shareProofs, + NamespaceID: appShareProof.NamespaceId, + RowProof: types.RowProof{ + RowRoots: rowRoots, + Proofs: rowProofs, + StartRow: appShareProof.RowProof.StartRow, + EndRow: appShareProof.RowProof.EndRow, + }, + NamespaceVersion: appShareProof.NamespaceVersion, + } +} diff --git a/share/eds/proofs_cache.go b/share/eds/proofs_cache.go new file mode 100644 index 0000000000..f73ebcdf80 --- /dev/null +++ b/share/eds/proofs_cache.go @@ -0,0 +1,358 @@ +package eds + +import ( + "context" + "errors" + "fmt" + "io" + "sync" + "sync/atomic" + + "github.com/ipfs/boxo/blockservice" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/nmt" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/ipld" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ AccessorStreamer = (*proofsCache)(nil) + +// proofsCache is eds accessor that caches proofs for rows and columns. It also caches extended +// axis Shares. It is used to speed up the process of building proofs for rows and columns, +// reducing the number of reads from the underlying accessor. Cache does not synchronize access +// to the underlying accessor. +type proofsCache struct { + inner AccessorStreamer + + // size caches the size of the data square + size atomic.Int32 + // dataHash caches the data hash + dataHash atomic.Pointer[share.DataHash] + // rootsCache caches the axis roots + rootsCache atomic.Pointer[share.AxisRoots] + // axisCacheLock protects proofCache + axisCacheLock sync.RWMutex + // axisCache caches the axis Shares and proofs. Index in the slice corresponds to the axis type. + // The map key is the index of the axis. + axisCache []map[int]axisWithProofs + + // disableCache disables caching of rows for testing purposes + disableCache bool +} + +// axisWithProofs is used to cache the extended axis Shares and proofs. +type axisWithProofs struct { + half AxisHalf + // shares are the extended axis Shares + shares []libshare.Share + // root caches the root of the tree. It will be set only when proofs are calculated + root []byte + // proofs are stored in a blockservice.BlockGetter by their CID. It will be set only when proofs + // are calculated and will be used to get the proof for a specific share. BlockGetter is used to + // reuse ipld based proof generation logic, which traverses the tree from the root to the leafs and + // collects the nodes on the path. This is temporary and will be replaced with a more efficient + // proof caching mechanism in nmt package, once it is implemented. + proofs blockservice.BlockGetter +} + +// WithProofsCache creates a new eds accessor with caching of proofs for rows and columns. It is +// used to speed up the process of building proofs for rows and columns, reducing the number of +// reads from the underlying accessor. +func WithProofsCache(ac AccessorStreamer) AccessorStreamer { + rows := make(map[int]axisWithProofs) + cols := make(map[int]axisWithProofs) + axisCache := []map[int]axisWithProofs{rows, cols} + return &proofsCache{ + inner: ac, + axisCache: axisCache, + } +} + +func (c *proofsCache) Size(ctx context.Context) int { + size := c.size.Load() + if size == 0 { + size = int32(c.inner.Size(ctx)) + c.size.Store(size) + } + return int(size) +} + +func (c *proofsCache) DataHash(ctx context.Context) (share.DataHash, error) { + dataHash := c.dataHash.Load() + if dataHash != nil { + return *dataHash, nil + } + loaded, err := c.inner.DataHash(ctx) + if err != nil { + return nil, err + } + c.dataHash.Store(&loaded) + return loaded, nil +} + +func (c *proofsCache) AxisRoots(ctx context.Context) (*share.AxisRoots, error) { + roots := c.rootsCache.Load() + if roots != nil { + return roots, nil + } + + // if roots are not in cache, read them from the inner accessor + roots, err := c.inner.AxisRoots(ctx) + if err != nil { + return nil, err + } + c.rootsCache.Store(roots) + return roots, nil +} + +func (c *proofsCache) Sample(ctx context.Context, idx shwap.SampleCoords) (shwap.Sample, error) { + axisType, axisIdx, shrIdx := rsmt2d.Row, idx.Row, idx.Col + ax, err := c.axisWithProofs(ctx, axisType, axisIdx) + if err != nil { + return shwap.Sample{}, err + } + + // build share proof from proofs cached for given axis + share := ax.shares[shrIdx] + proofs, err := ipld.GetProof(ctx, ax.proofs, ax.root, shrIdx, c.Size(ctx)) + if err != nil { + return shwap.Sample{}, fmt.Errorf("building proof from cache: %w", err) + } + + return shwap.Sample{ + Share: share, + Proof: &proofs, + ProofType: axisType, + }, nil +} + +func (c *proofsCache) axisWithProofs(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) (axisWithProofs, error) { + // return axis with proofs from cache if possible + ax, ok := c.getAxisFromCache(axisType, axisIdx) + if ax.proofs != nil { + // return axis with proofs from cache, only if proofs are already calculated + return ax, nil + } + + if !ok { + // if axis is not in cache, read it from the inner accessor + half, err := c.inner.AxisHalf(ctx, axisType, axisIdx) + if err != nil { + return axisWithProofs{}, fmt.Errorf("reading axis half from inner accessor: %w", err) + } + ax.half = half + } + + if len(ax.shares) == 0 { + shares, err := ax.half.Extended() + if err != nil { + return axisWithProofs{}, fmt.Errorf("reading axis shares: %w", err) + } + ax.shares = shares + } + + // build proofs from Shares and cache them + adder := ipld.NewProofsAdder(c.Size(ctx), true) + tree := wrapper.NewErasuredNamespacedMerkleTree( + uint64(c.Size(ctx)/2), + uint(axisIdx), + nmt.NodeVisitor(adder.VisitFn()), + ) + for _, shr := range ax.shares { + err := tree.Push(shr.ToBytes()) + if err != nil { + return axisWithProofs{}, fmt.Errorf("push shares: %w", err) + } + } + + // build the tree + root, err := tree.Root() + if err != nil { + return axisWithProofs{}, fmt.Errorf("calculating root: %w", err) + } + + ax.root = root + ax.proofs, err = newRowProofsGetter(adder.Proofs()) + if err != nil { + return axisWithProofs{}, fmt.Errorf("creating proof getter: %w", err) + } + + if !c.disableCache { + c.storeAxisInCache(axisType, axisIdx, ax) + } + return ax, nil +} + +func (c *proofsCache) AxisHalf(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) (AxisHalf, error) { + // return axis from cache if possible + ax, ok := c.getAxisFromCache(axisType, axisIdx) + if ok { + return ax.half, nil + } + + // read axis from inner accessor if axis is in the first quadrant + half, err := c.inner.AxisHalf(ctx, axisType, axisIdx) + if err != nil { + return AxisHalf{}, fmt.Errorf("reading axis from inner accessor: %w", err) + } + + if !c.disableCache { + ax.half = half + c.storeAxisInCache(axisType, axisIdx, ax) + } + + return half, nil +} + +func (c *proofsCache) RowNamespaceData( + ctx context.Context, + namespace libshare.Namespace, + rowIdx int, +) (shwap.RowNamespaceData, error) { + ax, err := c.axisWithProofs(ctx, rsmt2d.Row, rowIdx) + if err != nil { + return shwap.RowNamespaceData{}, err + } + + row, proof, err := ipld.GetSharesByNamespace(ctx, ax.proofs, ax.root, namespace, c.Size(ctx)) + if err != nil { + return shwap.RowNamespaceData{}, fmt.Errorf("shares by namespace %s for row %v: %w", namespace.String(), rowIdx, err) + } + + return shwap.RowNamespaceData{ + Shares: row, + Proof: proof, + }, nil +} + +func (c *proofsCache) Shares(ctx context.Context) ([]libshare.Share, error) { + odsSize := c.Size(ctx) / 2 + shares := make([]libshare.Share, 0, odsSize*odsSize) + for i := 0; i < c.Size(ctx)/2; i++ { + ax, err := c.AxisHalf(ctx, rsmt2d.Row, i) + if err != nil { + return nil, err + } + + half := ax.Shares + if ax.IsParity { + shares, err = c.axisShares(ctx, rsmt2d.Row, i) + if err != nil { + return nil, err + } + half = shares[:odsSize] + } + + shares = append(shares, half...) + } + return shares, nil +} + +func (c *proofsCache) Reader() (io.Reader, error) { + odsSize := c.Size(context.TODO()) / 2 + reader := NewShareReader(odsSize, c.getShare) + return reader, nil +} + +func (c *proofsCache) Close() error { + return c.inner.Close() +} + +func (c *proofsCache) axisShares(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) ([]libshare.Share, error) { + ax, ok := c.getAxisFromCache(axisType, axisIdx) + if ok && len(ax.shares) != 0 { + return ax.shares, nil + } + + if !ok { + // if axis is not in cache, read it from the inner accessor + half, err := c.inner.AxisHalf(ctx, axisType, axisIdx) + if err != nil { + return nil, fmt.Errorf("reading axis half from inner accessor: %w", err) + } + ax.half = half + } + + shares, err := ax.half.Extended() + if err != nil { + return nil, fmt.Errorf("extending shares: %w", err) + } + + if !c.disableCache { + ax.shares = shares + c.storeAxisInCache(axisType, axisIdx, ax) + } + return shares, nil +} + +func (c *proofsCache) storeAxisInCache(axisType rsmt2d.Axis, axisIdx int, axis axisWithProofs) { + c.axisCacheLock.Lock() + defer c.axisCacheLock.Unlock() + c.axisCache[axisType][axisIdx] = axis +} + +func (c *proofsCache) getAxisFromCache(axisType rsmt2d.Axis, axisIdx int) (axisWithProofs, bool) { + c.axisCacheLock.RLock() + defer c.axisCacheLock.RUnlock() + ax, ok := c.axisCache[axisType][axisIdx] + return ax, ok +} + +func (c *proofsCache) getShare(rowIdx, colIdx int) (libshare.Share, error) { + ctx := context.TODO() + odsSize := c.Size(ctx) / 2 + half, err := c.AxisHalf(ctx, rsmt2d.Row, rowIdx) + if err != nil { + return libshare.Share{}, fmt.Errorf("reading axis half: %w", err) + } + + // if share is from the same side of axis return share right away + if colIdx > odsSize == half.IsParity { + if half.IsParity { + colIdx -= odsSize + } + return half.Shares[colIdx], nil + } + + // if share index is from opposite part of axis, obtain full axis shares + shares, err := c.axisShares(ctx, rsmt2d.Row, rowIdx) + if err != nil { + return libshare.Share{}, fmt.Errorf("reading axis shares: %w", err) + } + return shares[colIdx], nil +} + +// rowProofsGetter implements blockservice.BlockGetter interface +type rowProofsGetter struct { + proofs map[cid.Cid]blocks.Block +} + +func newRowProofsGetter(rawProofs map[cid.Cid][]byte) (*rowProofsGetter, error) { + proofs := make(map[cid.Cid]blocks.Block, len(rawProofs)) + for k, v := range rawProofs { + b, err := blocks.NewBlockWithCid(v, k) + if err != nil { + return nil, err + } + proofs[k] = b + } + return &rowProofsGetter{proofs: proofs}, nil +} + +func (r rowProofsGetter) GetBlock(_ context.Context, c cid.Cid) (blocks.Block, error) { + if b, ok := r.proofs[c]; ok { + return b, nil + } + return nil, errors.New("block not found") +} + +func (r rowProofsGetter) GetBlocks(_ context.Context, _ []cid.Cid) <-chan blocks.Block { + panic("not implemented") +} diff --git a/share/eds/proofs_cache_test.go b/share/eds/proofs_cache_test.go new file mode 100644 index 0000000000..b570b15c1e --- /dev/null +++ b/share/eds/proofs_cache_test.go @@ -0,0 +1,27 @@ +package eds + +import ( + "context" + "testing" + "time" + + "github.com/celestiaorg/rsmt2d" +) + +func TestCache(t *testing.T) { + ODSSize := 16 + ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) + t.Cleanup(cancel) + + newAccessor := func(tb testing.TB, inner *rsmt2d.ExtendedDataSquare) Accessor { + accessor := &Rsmt2D{ExtendedDataSquare: inner} + return WithProofsCache(accessor) + } + TestSuiteAccessor(ctx, t, newAccessor, ODSSize) + + newAccessorStreamer := func(tb testing.TB, inner *rsmt2d.ExtendedDataSquare) AccessorStreamer { + accessor := &Rsmt2D{ExtendedDataSquare: inner} + return WithProofsCache(accessor) + } + TestStreamer(ctx, t, newAccessorStreamer, ODSSize) +} diff --git a/share/eds/read.go b/share/eds/read.go new file mode 100644 index 0000000000..26d1b9e237 --- /dev/null +++ b/share/eds/read.go @@ -0,0 +1,67 @@ +package eds + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" +) + +// ReadAccessor reads up EDS out of the io.Reader until io.EOF and provides. +func ReadAccessor(ctx context.Context, reader io.Reader, root *share.AxisRoots) (*Rsmt2D, error) { + odsSize := len(root.RowRoots) / 2 + shares, err := ReadShares(reader, libshare.ShareSize, odsSize) + if err != nil { + return nil, fmt.Errorf("failed to read eds from ods bytes: %w", err) + } + + // verify that the EDS hash matches the expected hash + rsmt2d, err := Rsmt2DFromShares(shares, odsSize) + if err != nil { + return nil, fmt.Errorf("failed to create rsmt2d from shares: %w", err) + } + datahash, err := rsmt2d.DataHash(ctx) + if err != nil { + return nil, fmt.Errorf("failed to calculate data hash: %w", err) + } + if !bytes.Equal(datahash, root.Hash()) { + return nil, fmt.Errorf( + "content integrity mismatch: imported root %s doesn't match expected root %s", + datahash, + root.Hash(), + ) + } + return rsmt2d, nil +} + +// ReadShares reads shares from the provided io.Reader until EOF. If EOF is reached, the remaining shares +// are populated as tail padding shares. Provided reader must contain shares in row-major order. +func ReadShares(r io.Reader, shareSize, odsSize int) ([]libshare.Share, error) { + shares := make([]libshare.Share, odsSize*odsSize) + var total int + for i := range shares { + shr := make([]byte, shareSize) + n, err := io.ReadFull(r, shr) + if errors.Is(err, io.EOF) { + for ; i < len(shares); i++ { + shares[i] = libshare.TailPaddingShare() + } + return shares, nil + } + if err != nil { + return nil, fmt.Errorf("reading shares: %w, bytes read: %v", err, total+n) + } + newShare, err := libshare.NewShare(shr) + if err != nil { + return nil, err + } + shares[i] = *newShare + total += n + } + return shares, nil +} diff --git a/share/eds/retriever.go b/share/eds/retriever.go index 606746cbc8..926aa2ed9a 100644 --- a/share/eds/retriever.go +++ b/share/eds/retriever.go @@ -15,8 +15,8 @@ import ( "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" @@ -57,18 +57,18 @@ func NewRetriever(bServ blockservice.BlockService) *Retriever { // data square and reconstructs the other three quadrants (3/4). If the requested quadrant is not // available within RetrieveQuadrantTimeout, it starts requesting another quadrant until either the // data is reconstructed, context is canceled or ErrByzantine is generated. -func (r *Retriever) Retrieve(ctx context.Context, dah *da.DataAvailabilityHeader) (*rsmt2d.ExtendedDataSquare, error) { +func (r *Retriever) Retrieve(ctx context.Context, roots *share.AxisRoots) (*rsmt2d.ExtendedDataSquare, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() // cancels all the ongoing requests if reconstruction succeeds early ctx, span := tracer.Start(ctx, "retrieve-square") defer span.End() span.SetAttributes( - attribute.Int("size", len(dah.RowRoots)), + attribute.Int("size", len(roots.RowRoots)), ) - log.Debugw("retrieving data square", "data_hash", dah.String(), "size", len(dah.RowRoots)) - ses, err := r.newSession(ctx, dah) + log.Debugw("retrieving data square", "data_hash", roots.String(), "size", len(roots.RowRoots)) + ses, err := r.newSession(ctx, roots) if err != nil { return nil, err } @@ -91,7 +91,7 @@ func (r *Retriever) Retrieve(ctx context.Context, dah *da.DataAvailabilityHeader // nmt proofs computed during the session ses.close(false) span.RecordError(err) - return nil, byzantine.NewErrByzantine(ctx, r.bServ.Blockstore(), dah, errByz) + return nil, byzantine.NewErrByzantine(ctx, r.bServ.Blockstore(), roots, errByz) } log.Warnw("not enough shares to reconstruct data square, requesting more...", "err", err) @@ -107,7 +107,7 @@ func (r *Retriever) Retrieve(ctx context.Context, dah *da.DataAvailabilityHeader // quadrant request retries. Also, provides an API // to reconstruct the block once enough shares are fetched. type retrievalSession struct { - dah *da.DataAvailabilityHeader + roots *share.AxisRoots bget blockservice.BlockGetter adder *ipld.NmtNodeAdder @@ -115,7 +115,7 @@ type retrievalSession struct { // https://github.com/celestiaorg/rsmt2d/issues/135 squareQuadrants []*quadrant squareCellsLks [][]sync.Mutex - squareCellsCount uint32 + squareCellsCount atomic.Uint32 squareSig chan struct{} squareDn chan struct{} squareLk sync.RWMutex @@ -125,8 +125,8 @@ type retrievalSession struct { } // newSession creates a new retrieval session and kicks off requesting process. -func (r *Retriever) newSession(ctx context.Context, dah *da.DataAvailabilityHeader) (*retrievalSession, error) { - size := len(dah.RowRoots) +func (r *Retriever) newSession(ctx context.Context, roots *share.AxisRoots) (*retrievalSession, error) { + size := len(roots.RowRoots) adder := ipld.NewNmtNodeAdder(ctx, r.bServ, ipld.MaxSizeBatchOption(size)) proofsVisitor := ipld.ProofsAdderFromCtx(ctx).VisitFn() @@ -143,16 +143,16 @@ func (r *Retriever) newSession(ctx context.Context, dah *da.DataAvailabilityHead return &tree } - square, err := rsmt2d.NewExtendedDataSquare(share.DefaultRSMT2DCodec(), treeFn, uint(size), share.Size) + square, err := rsmt2d.NewExtendedDataSquare(share.DefaultRSMT2DCodec(), treeFn, uint(size), libshare.ShareSize) if err != nil { return nil, err } ses := &retrievalSession{ - dah: dah, + roots: roots, bget: blockservice.NewSession(ctx, r.bServ), adder: adder, - squareQuadrants: newQuadrants(dah), + squareQuadrants: newQuadrants(roots), squareCellsLks: make([][]sync.Mutex, size), squareSig: make(chan struct{}, 1), squareDn: make(chan struct{}), @@ -187,12 +187,12 @@ func (rs *retrievalSession) Reconstruct(ctx context.Context) (*rsmt2d.ExtendedDa defer span.End() // and try to repair with what we have - err := rs.square.Repair(rs.dah.RowRoots, rs.dah.ColumnRoots) + err := rs.square.Repair(rs.roots.RowRoots, rs.roots.ColumnRoots) if err != nil { span.RecordError(err) return nil, err } - log.Infow("data square reconstructed", "data_hash", rs.dah.String(), "size", len(rs.dah.RowRoots)) + log.Infow("data square reconstructed", "data_hash", rs.roots.String(), "size", len(rs.roots.RowRoots)) close(rs.squareDn) return rs.square, nil } @@ -280,7 +280,7 @@ func (rs *retrievalSession) doRequest(ctx context.Context, q *quadrant) { // and go get shares of left or the right side of the whole col/row axis // the left or the right side of the tree represent some portion of the quadrant // which we put into the rs.square share-by-share by calculating shares' indexes using q.index - ipld.GetShares(ctx, rs.bget, nd.Links()[q.x].Cid, size, func(j int, share share.Share) { + ipld.GetShares(ctx, rs.bget, nd.Links()[q.x].Cid, size, func(j int, rawShare []byte) { // NOTE: Each share can appear twice here, for a Row and Col, respectively. // These shares are always equal, and we allow only the first one to be written // in the square. @@ -307,7 +307,7 @@ func (rs *retrievalSession) doRequest(ctx context.Context, q *quadrant) { if rs.isReconstructed() { return } - if err := rs.square.SetCell(uint(x), uint(y), share); err != nil { + if err := rs.square.SetCell(uint(x), uint(y), rawShare); err != nil { // safe to ignore as: // * share size already verified // * the same share might come from either Row or Col @@ -319,7 +319,7 @@ func (rs *retrievalSession) doRequest(ctx context.Context, q *quadrant) { // but it is totally fine for the happy case and for now. // The earlier we correctly know that we have the full square - the earlier // we cancel ongoing requests - the less data is being wastedly transferred. - if atomic.AddUint32(&rs.squareCellsCount, 1) >= uint32(size*size) { + if rs.squareCellsCount.Add(1) >= uint32(size*size) { select { case rs.squareSig <- struct{}{}: default: diff --git a/share/eds/retriever_no_race_test.go b/share/eds/retriever_no_race_test.go index 73f86d555a..abe44220b4 100644 --- a/share/eds/retriever_no_race_test.go +++ b/share/eds/retriever_no_race_test.go @@ -9,8 +9,8 @@ import ( "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" @@ -31,7 +31,7 @@ func TestRetriever_ByzantineError(t *testing.T) { require.NoError(t, err) // corrupt shares so that eds erasure coding does not match - copy(shares[14][share.NamespaceSize:], shares[15][share.NamespaceSize:]) + copy(shares[14][libshare.NamespaceSize:], shares[15][libshare.NamespaceSize:]) // import corrupted eds batchAdder := ipld.NewNmtNodeAdder(ctx, bserv, ipld.MaxSizeBatchOption(width*2)) @@ -46,10 +46,10 @@ func TestRetriever_ByzantineError(t *testing.T) { require.NoError(t, err) // ensure we rcv an error - dah, err := da.NewDataAvailabilityHeader(attackerEDS) + roots, err := share.NewAxisRoots(attackerEDS) require.NoError(t, err) r := NewRetriever(bserv) - _, err = r.Retrieve(ctx, &dah) + _, err = r.Retrieve(ctx, roots) var errByz *byzantine.ErrByzantine require.ErrorAs(t, err, &errByz) } diff --git a/share/eds/retriever_quadrant.go b/share/eds/retriever_quadrant.go index 64cab6366c..97a5b332f2 100644 --- a/share/eds/retriever_quadrant.go +++ b/share/eds/retriever_quadrant.go @@ -6,9 +6,9 @@ import ( "github.com/ipfs/go-cid" - "github.com/celestiaorg/celestia-app/v2/pkg/da" "github.com/celestiaorg/rsmt2d" + "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/ipld" ) @@ -49,11 +49,11 @@ type quadrant struct { // newQuadrants constructs a slice of quadrants from DAHeader. // There are always 4 quadrants per each source (row and col), so 8 in total. // The ordering of quadrants is random. -func newQuadrants(dah *da.DataAvailabilityHeader) []*quadrant { +func newQuadrants(roots *share.AxisRoots) []*quadrant { // combine all the roots into one slice, so they can be easily accessible by index daRoots := [][][]byte{ - dah.RowRoots, - dah.ColumnRoots, + roots.RowRoots, + roots.ColumnRoots, } // create a quadrant slice for each source(row;col) sources := [][]*quadrant{ diff --git a/share/eds/retriever_test.go b/share/eds/retriever_test.go index 5c098da55e..b9bcb4f74f 100644 --- a/share/eds/retriever_test.go +++ b/share/eds/retriever_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" @@ -20,7 +20,6 @@ import ( "github.com/celestiaorg/celestia-node/share/eds/byzantine" "github.com/celestiaorg/celestia-node/share/eds/edstest" "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/sharetest" ) func TestRetriever_Retrieve(t *testing.T) { @@ -47,7 +46,8 @@ func TestRetriever_Retrieve(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // generate EDS - shares := sharetest.RandShares(t, tc.squareSize*tc.squareSize) + shares, err := libshare.RandShares(tc.squareSize * tc.squareSize) + require.NoError(t, err) in, err := ipld.AddShares(ctx, shares, bServ) require.NoError(t, err) @@ -55,9 +55,9 @@ func TestRetriever_Retrieve(t *testing.T) { ctx, cancel := context.WithTimeout(ctx, time.Minute*5) // the timeout is big for the max size which is long defer cancel() - dah, err := da.NewDataAvailabilityHeader(in) + roots, err := share.NewAxisRoots(in) require.NoError(t, err) - out, err := r.Retrieve(ctx, &dah) + out, err := r.Retrieve(ctx, roots) require.NoError(t, err) assert.True(t, in.Equals(out)) }) @@ -76,13 +76,14 @@ func TestRetriever_MultipleRandQuadrants(t *testing.T) { r := NewRetriever(bServ) // generate EDS - shares := sharetest.RandShares(t, squareSize*squareSize) + shares, err := libshare.RandShares(squareSize * squareSize) + require.NoError(t, err) in, err := ipld.AddShares(ctx, shares, bServ) require.NoError(t, err) - dah, err := da.NewDataAvailabilityHeader(in) + roots, err := share.NewAxisRoots(in) require.NoError(t, err) - ses, err := r.newSession(ctx, &dah) + ses, err := r.newSession(ctx, roots) require.NoError(t, err) // wait until two additional quadrants requested diff --git a/share/eds/rsmt2d.go b/share/eds/rsmt2d.go new file mode 100644 index 0000000000..1d806324b5 --- /dev/null +++ b/share/eds/rsmt2d.go @@ -0,0 +1,176 @@ +package eds + +import ( + "context" + "fmt" + "io" + + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ AccessorStreamer = (*Rsmt2D)(nil) + +// Rsmt2D is a rsmt2d based in-memory implementation of Accessor. +type Rsmt2D struct { + *rsmt2d.ExtendedDataSquare +} + +// Size returns the size of the Extended Data Square. +func (eds *Rsmt2D) Size(context.Context) int { + return int(eds.Width()) +} + +// DataHash returns data hash of the Accessor. +func (eds *Rsmt2D) DataHash(context.Context) (share.DataHash, error) { + roots, err := share.NewAxisRoots(eds.ExtendedDataSquare) + if err != nil { + return share.DataHash{}, fmt.Errorf("while creating data root: %w", err) + } + return roots.Hash(), nil +} + +// AxisRoots returns AxisRoots of the Accessor. +func (eds *Rsmt2D) AxisRoots(context.Context) (*share.AxisRoots, error) { + roots, err := share.NewAxisRoots(eds.ExtendedDataSquare) + if err != nil { + return nil, fmt.Errorf("while creating axis roots: %w", err) + } + return roots, nil +} + +// Sample returns share and corresponding proof for row and column indices. +func (eds *Rsmt2D) Sample( + _ context.Context, + idx shwap.SampleCoords, +) (shwap.Sample, error) { + return eds.SampleForProofAxis(idx, rsmt2d.Row) +} + +// SampleForProofAxis samples a share from an Extended Data Square based on the provided +// row and column indices and proof axis. It returns a sample with the share and proof. +func (eds *Rsmt2D) SampleForProofAxis( + idx shwap.SampleCoords, + proofType rsmt2d.Axis, +) (shwap.Sample, error) { + axisIdx, shrIdx := relativeIndexes(idx.Row, idx.Col, proofType) + shares, err := getAxis(eds.ExtendedDataSquare, proofType, axisIdx) + if err != nil { + return shwap.Sample{}, err + } + + tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(eds.Width()/2), uint(axisIdx)) + for _, shr := range shares { + err := tree.Push(shr.ToBytes()) + if err != nil { + return shwap.Sample{}, fmt.Errorf("while pushing shares to NMT: %w", err) + } + } + + prf, err := tree.ProveRange(shrIdx, shrIdx+1) + if err != nil { + return shwap.Sample{}, fmt.Errorf("while proving range share over NMT: %w", err) + } + + return shwap.Sample{ + Share: shares[shrIdx], + Proof: &prf, + ProofType: proofType, + }, nil +} + +// AxisHalf returns Shares for the first half of the axis of the given type and index. +func (eds *Rsmt2D) AxisHalf(_ context.Context, axisType rsmt2d.Axis, axisIdx int) (AxisHalf, error) { + shares, err := getAxis(eds.ExtendedDataSquare, axisType, axisIdx) + if err != nil { + return AxisHalf{}, fmt.Errorf("while getting axis share: %w", err) + } + halfShares := shares[:eds.Width()/2] + return AxisHalf{ + Shares: halfShares, + IsParity: false, + }, nil +} + +// HalfRow constructs a new shwap.Row from an Extended Data Square based on the specified index and +// side. +func (eds *Rsmt2D) HalfRow(idx int, side shwap.RowSide) (shwap.Row, error) { + return shwap.RowFromEDS(eds.ExtendedDataSquare, idx, side) +} + +// RowNamespaceData returns data for the given namespace and row index. +func (eds *Rsmt2D) RowNamespaceData( + _ context.Context, + namespace libshare.Namespace, + rowIdx int, +) (shwap.RowNamespaceData, error) { + shares := eds.Row(uint(rowIdx)) + sh, err := libshare.FromBytes(shares) + if err != nil { + return shwap.RowNamespaceData{}, fmt.Errorf("while converting shares from bytes: %w", err) + } + return shwap.RowNamespaceDataFromShares(sh, namespace, rowIdx) +} + +// Shares returns data (ODS) shares extracted from the EDS. It returns new copy of the shares each +// time. +func (eds *Rsmt2D) Shares(_ context.Context) ([]libshare.Share, error) { + return libshare.FromBytes(eds.ExtendedDataSquare.FlattenedODS()) +} + +func (eds *Rsmt2D) Close() error { + return nil +} + +// Reader returns binary reader for the file. +func (eds *Rsmt2D) Reader() (io.Reader, error) { + getShare := func(rowIdx, colIdx int) (libshare.Share, error) { + rawShare := eds.GetCell(uint(rowIdx), uint(colIdx)) + + sh, err := libshare.NewShare(rawShare) + if err != nil { + return libshare.Share{}, fmt.Errorf("while creating share: %w", err) + } + return *sh, nil + } + odsSize := int(eds.Width() / 2) + reader := NewShareReader(odsSize, getShare) + return reader, nil +} + +// Rsmt2DFromShares constructs an Extended Data Square from shares. +func Rsmt2DFromShares(shares []libshare.Share, odsSize int) (*Rsmt2D, error) { + treeFn := wrapper.NewConstructor(uint64(odsSize)) + eds, err := rsmt2d.ComputeExtendedDataSquare(libshare.ToBytes(shares), share.DefaultRSMT2DCodec(), treeFn) + if err != nil { + return &Rsmt2D{}, fmt.Errorf("computing extended data square: %w", err) + } + + return &Rsmt2D{eds}, nil +} + +func getAxis(eds *rsmt2d.ExtendedDataSquare, axisType rsmt2d.Axis, axisIdx int) ([]libshare.Share, error) { + switch axisType { + case rsmt2d.Row: + return libshare.FromBytes(eds.Row(uint(axisIdx))) + case rsmt2d.Col: + return libshare.FromBytes(eds.Col(uint(axisIdx))) + default: + panic("unknown axis") + } +} + +func relativeIndexes(rowIdx, colIdx int, axisType rsmt2d.Axis) (axisIdx, shrIdx int) { + switch axisType { + case rsmt2d.Row: + return rowIdx, colIdx + case rsmt2d.Col: + return colIdx, rowIdx + default: + panic(fmt.Sprintf("invalid proof type: %d", axisType)) + } +} diff --git a/share/eds/rsmt2d_test.go b/share/eds/rsmt2d_test.go new file mode 100644 index 0000000000..89fd316717 --- /dev/null +++ b/share/eds/rsmt2d_test.go @@ -0,0 +1,80 @@ +package eds + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestRsmt2dAccessor(t *testing.T) { + odsSize := 16 + newAccessor := func(tb testing.TB, eds *rsmt2d.ExtendedDataSquare) Accessor { + return &Rsmt2D{ExtendedDataSquare: eds} + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) + t.Cleanup(cancel) + + TestSuiteAccessor(ctx, t, newAccessor, odsSize) + + newStreamer := func(tb testing.TB, eds *rsmt2d.ExtendedDataSquare) AccessorStreamer { + return &Rsmt2D{ExtendedDataSquare: eds} + } + TestStreamer(ctx, t, newStreamer, odsSize) +} + +func TestRsmt2dHalfRow(t *testing.T) { + const odsSize = 8 + eds, _ := randRsmt2dAccsessor(t, odsSize) + + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for _, side := range []shwap.RowSide{shwap.Left, shwap.Right} { + row, err := eds.HalfRow(rowIdx, side) + require.NoError(t, err) + + want := eds.Row(uint(rowIdx)) + shares, err := row.Shares() + require.NoError(t, err) + require.Equal(t, want, libshare.ToBytes(shares)) + } + } +} + +func TestRsmt2dSampleForProofAxis(t *testing.T) { + const odsSize = 8 + eds := edstest.RandEDS(t, odsSize) + accessor := Rsmt2D{ExtendedDataSquare: eds} + + for _, proofType := range []rsmt2d.Axis{rsmt2d.Row, rsmt2d.Col} { + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for colIdx := 0; colIdx < odsSize*2; colIdx++ { + idx := shwap.SampleCoords{Row: rowIdx, Col: colIdx} + + sample, err := accessor.SampleForProofAxis(idx, proofType) + require.NoError(t, err) + + want := eds.GetCell(uint(rowIdx), uint(colIdx)) + require.Equal(t, want, sample.Share.ToBytes()) + require.Equal(t, proofType, sample.ProofType) + require.NotNil(t, sample.Proof) + require.Equal(t, sample.Proof.End()-sample.Proof.Start(), 1) + require.Len(t, sample.Proof.Nodes(), 4) + } + } + } +} + +func randRsmt2dAccsessor(t *testing.T, size int) (Rsmt2D, *share.AxisRoots) { + eds := edstest.RandEDS(t, size) + root, err := share.NewAxisRoots(eds) + require.NoError(t, err) + return Rsmt2D{ExtendedDataSquare: eds}, root +} diff --git a/share/eds/share_reader.go b/share/eds/share_reader.go new file mode 100644 index 0000000000..9fdd83e393 --- /dev/null +++ b/share/eds/share_reader.go @@ -0,0 +1,82 @@ +package eds + +import ( + "bytes" + "errors" + "fmt" + "io" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +// ShareReader implement io.Reader over general function that gets shares by +// their respective Row and Col coordinates. +// It enables share streaming over arbitrary storages. +type ShareReader struct { + // getShare general share getting function for share retrieval + getShare func(rowIdx, colIdx int) (libshare.Share, error) + + // buf buffers shares from partial reads with default size + buf *bytes.Buffer + // current is the amount of Shares stored in square that have been written by squareCopy. When + // current reaches total, squareCopy will prevent further reads by returning io.EOF + current, odsSize, total int +} + +// NewShareReader constructs a new ShareGetter from underlying ODS size and general share getting function. +func NewShareReader(odsSize int, getShare func(rowIdx, colIdx int) (libshare.Share, error)) *ShareReader { + return &ShareReader{ + getShare: getShare, + buf: bytes.NewBuffer(nil), + odsSize: odsSize, + total: odsSize * odsSize, + } +} + +func (r *ShareReader) Read(p []byte) (int, error) { + if r.current >= r.total && r.buf.Len() == 0 { + return 0, io.EOF + } + // if provided array is smaller than data in buf, read from buf + if len(p) <= r.buf.Len() { + return r.buf.Read(p) + } + n, err := io.ReadFull(r.buf, p) + if err == nil { + return n, nil + } + if !errors.Is(err, io.ErrUnexpectedEOF) && !errors.Is(err, io.EOF) { + return n, fmt.Errorf("unexpected error reading from buf: %w", err) + } + + written := n + for r.current < r.total { + rowIdx, colIdx := r.current/r.odsSize, r.current%r.odsSize + share, err := r.getShare(rowIdx, colIdx) + if err != nil { + return 0, fmt.Errorf("get share: %w", err) + } + rawShare := share.ToBytes() + // copy share to provided buffer + emptySpace := len(p) - written + r.current++ + if len(rawShare) < emptySpace { + n := copy(p[written:], rawShare) + written += n + continue + } + + // if share didn't fit into buffer fully, store remaining bytes into inner buf + n := copy(p[written:], rawShare[:emptySpace]) + written += n + n, err = r.buf.Write(rawShare[emptySpace:]) + if err != nil { + return 0, fmt.Errorf("write share to inner buffer: %w", err) + } + if n != len(rawShare)-emptySpace { + return 0, fmt.Errorf("share was not written fully: %w", io.ErrShortWrite) + } + return written, nil + } + return written, nil +} diff --git a/share/eds/share_reader_test.go b/share/eds/share_reader_test.go new file mode 100644 index 0000000000..ea2fba7cda --- /dev/null +++ b/share/eds/share_reader_test.go @@ -0,0 +1,60 @@ +package eds + +import ( + "errors" + "io" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share/eds/edstest" +) + +func TestShareReader(t *testing.T) { + // create io.Writer that write random data + odsSize := 16 + eds := edstest.RandEDS(t, odsSize) + getShare := func(rowIdx, colIdx int) (libshare.Share, error) { + rawShare := eds.GetCell(uint(rowIdx), uint(colIdx)) + sh, err := libshare.NewShare(rawShare) + if err != nil { + return libshare.Share{}, err + } + return *sh, nil + } + + reader := NewShareReader(odsSize, getShare) + readBytes, err := readWithRandomBuffer(reader, 1024) + require.NoError(t, err) + expected := make([]byte, 0, odsSize*odsSize*libshare.ShareSize) + for _, share := range eds.FlattenedODS() { + expected = append(expected, share...) + } + require.Len(t, readBytes, len(expected)) + require.Equal(t, expected, readBytes) +} + +// testRandReader reads from reader with buffers of random sizes. +func readWithRandomBuffer(reader io.Reader, maxBufSize int) ([]byte, error) { + // create buffer of random size + data := make([]byte, 0, maxBufSize) + for { + bufSize := rand.Intn(maxBufSize-1) + 1 + buf := make([]byte, bufSize) + n, err := reader.Read(buf) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + if n < bufSize { + buf = buf[:n] + } + data = append(data, buf...) + if errors.Is(err, io.EOF) { + break + } + } + return data, nil +} diff --git a/share/eds/store.go b/share/eds/store.go deleted file mode 100644 index 249555d69e..0000000000 --- a/share/eds/store.go +++ /dev/null @@ -1,650 +0,0 @@ -package eds - -import ( - "bufio" - "bytes" - "context" - "errors" - "fmt" - "io" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/filecoin-project/dagstore" - "github.com/filecoin-project/dagstore/index" - "github.com/filecoin-project/dagstore/mount" - "github.com/filecoin-project/dagstore/shard" - bstore "github.com/ipfs/boxo/blockstore" - "github.com/ipfs/go-datastore" - carv1 "github.com/ipld/go-car" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds/cache" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -const ( - blocksPath = "/blocks/" - indexPath = "/index/" - transientsPath = "/transients/" -) - -var ErrNotFound = errors.New("eds not found in store") - -// Store maintains (via DAGStore) a top-level index enabling granular and efficient random access to -// every share and/or Merkle proof over every registered CARv1 file. The EDSStore provides a custom -// blockstore interface implementation to achieve access. The main use-case is randomized sampling -// over the whole chain of EDS block data and getting data by namespace. -type Store struct { - cancel context.CancelFunc - - dgstr *dagstore.DAGStore - mounts *mount.Registry - - bs *blockstore - cache atomic.Pointer[cache.DoubleCache] - - carIdx index.FullIndexRepo - invertedIdx *simpleInvertedIndex - - basepath string - gcInterval time.Duration - // lastGCResult is only stored on the store for testing purposes. - lastGCResult atomic.Pointer[dagstore.GCResult] - - // stripedLocks is used to synchronize parallel operations - stripedLocks [256]sync.Mutex - shardFailures chan dagstore.ShardResult - - metrics *metrics -} - -// NewStore creates a new EDS Store under the given basepath and datastore. -func NewStore(params *Parameters, basePath string, ds datastore.Batching) (*Store, error) { - if err := params.Validate(); err != nil { - return nil, err - } - - err := setupPath(basePath) - if err != nil { - return nil, fmt.Errorf("failed to setup eds.Store directories: %w", err) - } - - r := mount.NewRegistry() - err = r.Register("fs", &inMemoryOnceMount{}) - if err != nil { - return nil, fmt.Errorf("failed to register memory mount on the registry: %w", err) - } - if err != nil { - return nil, fmt.Errorf("failed to register FS mount on the registry: %w", err) - } - - fsRepo, err := index.NewFSRepo(basePath + indexPath) - if err != nil { - return nil, fmt.Errorf("failed to create index repository: %w", err) - } - - invertedIdx, err := newSimpleInvertedIndex(basePath) - if err != nil { - return nil, fmt.Errorf("failed to create index: %w", err) - } - - failureChan := make(chan dagstore.ShardResult) - dagStore, err := dagstore.NewDAGStore( - dagstore.Config{ - TransientsDir: basePath + transientsPath, - IndexRepo: fsRepo, - Datastore: ds, - MountRegistry: r, - TopLevelIndex: invertedIdx, - FailureCh: failureChan, - }, - ) - if err != nil { - return nil, fmt.Errorf("failed to create DAGStore: %w", err) - } - - recentBlocksCache, err := cache.NewAccessorCache("recent", params.RecentBlocksCacheSize) - if err != nil { - return nil, fmt.Errorf("failed to create recent blocks cache: %w", err) - } - - blockstoreCache, err := cache.NewAccessorCache("blockstore", params.BlockstoreCacheSize) - if err != nil { - return nil, fmt.Errorf("failed to create blockstore cache: %w", err) - } - - store := &Store{ - basepath: basePath, - dgstr: dagStore, - carIdx: fsRepo, - invertedIdx: invertedIdx, - gcInterval: params.GCInterval, - mounts: r, - shardFailures: failureChan, - } - store.bs = newBlockstore(store, ds) - store.cache.Store(cache.NewDoubleCache(recentBlocksCache, blockstoreCache)) - return store, nil -} - -func (s *Store) Start(ctx context.Context) error { - err := s.dgstr.Start(ctx) - if err != nil { - return err - } - // start Store only if DagStore succeeds - runCtx, cancel := context.WithCancel(context.Background()) - s.cancel = cancel - // initialize empty gc result to avoid panic on access - s.lastGCResult.Store(&dagstore.GCResult{ - Shards: make(map[shard.Key]error), - }) - - if s.gcInterval != 0 { - go s.gc(runCtx) - } - - go s.watchForFailures(runCtx) - return nil -} - -// Stop stops the underlying DAGStore. -func (s *Store) Stop(context.Context) error { - defer s.cancel() - - if err := s.metrics.close(); err != nil { - log.Warnw("failed to close metrics", "err", err) - } - - if err := s.invertedIdx.close(); err != nil { - return err - } - return s.dgstr.Close() -} - -// gc periodically removes all inactive or errored shards. -func (s *Store) gc(ctx context.Context) { - ticker := time.NewTicker(s.gcInterval) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - tnow := time.Now() - res, err := s.dgstr.GC(ctx) - s.metrics.observeGCtime(ctx, time.Since(tnow), err != nil) - if err != nil { - log.Errorf("garbage collecting dagstore: %v", err) - return - } - s.lastGCResult.Store(res) - } - } -} - -func (s *Store) watchForFailures(ctx context.Context) { - for { - select { - case <-ctx.Done(): - return - case res := <-s.shardFailures: - log.Errorw("removing shard after failure", "key", res.Key, "err", res.Error) - s.metrics.observeShardFailure(ctx, res.Key.String()) - k := share.MustDataHashFromString(res.Key.String()) - err := s.Remove(ctx, k) - if err != nil { - log.Errorw("failed to remove shard after failure", "key", res.Key, "err", err) - } - } - } -} - -// Put stores the given data square with DataRoot's hash as a key. -// -// The square is verified on the Exchange level, and Put only stores the square, trusting it. -// The resulting file stores all the shares and NMT Merkle Proofs of the EDS. -// Additionally, the file gets indexed s.t. store.Blockstore can access them. -func (s *Store) Put(ctx context.Context, root share.DataHash, square *rsmt2d.ExtendedDataSquare) error { - ctx, span := tracer.Start(ctx, "store/put", trace.WithAttributes( - attribute.Int("width", int(square.Width())), - )) - - tnow := time.Now() - err := s.put(ctx, root, square) - result := putOK - switch { - case errors.Is(err, dagstore.ErrShardExists): - result = putExists - case err != nil: - result = putFailed - } - utils.SetStatusAndEnd(span, err) - s.metrics.observePut(ctx, time.Since(tnow), result, square.Width()) - return err -} - -func (s *Store) put(ctx context.Context, root share.DataHash, square *rsmt2d.ExtendedDataSquare) (err error) { - lk := &s.stripedLocks[root[len(root)-1]] - lk.Lock() - defer lk.Unlock() - - // if root already exists, short-circuit - if has, _ := s.Has(ctx, root); has { - return dagstore.ErrShardExists - } - - key := root.String() - f, err := os.OpenFile(s.basepath+blocksPath+key, os.O_CREATE|os.O_WRONLY, 0o600) - if err != nil { - return err - } - defer closeAndLog("car file", f) - - // save encoded eds into buffer - mount := &inMemoryOnceMount{ - // TODO: buffer could be pre-allocated with capacity calculated based on eds size. - buf: bytes.NewBuffer(nil), - FileMount: mount.FileMount{Path: s.basepath + blocksPath + key}, - } - err = WriteEDS(ctx, square, mount) - if err != nil { - return fmt.Errorf("failed to write EDS to file: %w", err) - } - - // write whole buffered mount data in one go to optimize i/o - if _, err = mount.WriteTo(f); err != nil { - return fmt.Errorf("failed to write EDS to file: %w", err) - } - - ch := make(chan dagstore.ShardResult, 1) - err = s.dgstr.RegisterShard(ctx, shard.KeyFromString(key), mount, ch, dagstore.RegisterOpts{}) - if err != nil { - return fmt.Errorf("failed to initiate shard registration: %w", err) - } - - var result dagstore.ShardResult - select { - case result = <-ch: - case <-ctx.Done(): - // if the context finished before the result was received, track the result in a separate goroutine - go trackLateResult("put", ch, s.metrics, time.Minute*5) - return ctx.Err() - } - - if result.Error != nil { - return fmt.Errorf("failed to register shard: %w", result.Error) - } - - // the accessor returned in the result will be nil, so the shard needs to be acquired first to - // become available in the cache. It might take some time, and the result should not affect the put - // operation, so do it in a goroutine - // TODO: Ideally, only recent blocks should be put in the cache, but there is no way right now to - // check such a condition. - go func() { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - ac, err := s.cache.Load().First().GetOrLoad(ctx, result.Key, s.getAccessor) - if err != nil { - log.Warnw("unable to put accessor to recent blocks accessors cache", "err", err) - return - } - - // need to close returned accessor to remove the reader reference - if err := ac.Close(); err != nil { - log.Warnw("unable to close accessor after loading", "err", err) - } - }() - - return nil -} - -// waitForResult waits for a result from the res channel for a maximum duration specified by -// maxWait. If the result is not received within the specified duration, it logs an error -// indicating that the parent context has expired and the shard registration is stuck. If a result -// is received, it checks for any error and logs appropriate messages. -func trackLateResult(opName string, res <-chan dagstore.ShardResult, metrics *metrics, maxWait time.Duration) { - tnow := time.Now() - select { - case <-time.After(maxWait): - metrics.observeLongOp(context.Background(), opName, time.Since(tnow), longOpUnresolved) - log.Errorf("parent context is expired, while register shard is stuck for more than %v sec", time.Since(tnow)) - return - case result := <-res: - // don't observe if result was received right after launch of the func - if time.Since(tnow) < time.Second { - return - } - if result.Error != nil { - metrics.observeLongOp(context.Background(), opName, time.Since(tnow), longOpFailed) - log.Errorf("failed to register shard after context expired: %v ago, err: %s", time.Since(tnow), result.Error) - return - } - metrics.observeLongOp(context.Background(), opName, time.Since(tnow), longOpOK) - log.Warnf("parent context expired, but register shard finished with no error,"+ - " after context expired: %v ago", time.Since(tnow)) - return - } -} - -// GetCAR takes a DataRoot and returns a buffered reader to the respective EDS serialized as a -// CARv1 file. -// The Reader strictly reads the CAR header and first quadrant (1/4) of the EDS, omitting all the -// NMT Merkle proofs. Integrity of the store data is not verified. -// -// The shard is cached in the Store, so subsequent calls to GetCAR with the same root will use the -// same reader. The cache is responsible for closing the underlying reader. -func (s *Store) GetCAR(ctx context.Context, root share.DataHash) (io.ReadCloser, error) { - ctx, span := tracer.Start(ctx, "store/get-car") - tnow := time.Now() - r, err := s.getCAR(ctx, root) - s.metrics.observeGetCAR(ctx, time.Since(tnow), err != nil) - utils.SetStatusAndEnd(span, err) - return r, err -} - -func (s *Store) getCAR(ctx context.Context, root share.DataHash) (io.ReadCloser, error) { - key := shard.KeyFromString(root.String()) - accessor, err := s.cache.Load().Get(key) - if err == nil { - return newReadCloser(accessor), nil - } - // If the accessor is not found in the cache, create a new one from dagstore. We don't put the - // accessor in the cache here because getCAR is used by shrex-eds. There is a lower probability, - // compared to other cache put triggers, that the same block will be requested again soon. - shardAccessor, err := s.getAccessor(ctx, key) - if err != nil { - return nil, fmt.Errorf("failed to get accessor: %w", err) - } - - return newReadCloser(shardAccessor), nil -} - -// Blockstore returns an IPFS blockstore providing access to individual shares/nodes of all EDS -// registered on the Store. NOTE: The blockstore does not store whole Celestia Blocks but IPFS -// blocks. We represent `shares` and NMT Merkle proofs as IPFS blocks and IPLD nodes so Bitswap can -// access those. -func (s *Store) Blockstore() bstore.Blockstore { - return s.bs -} - -// CARBlockstore returns an IPFS Blockstore providing access to individual shares/nodes of a -// specific EDS identified by DataHash and registered on the Store. NOTE: The Blockstore does not -// store whole Celestia Blocks but IPFS blocks. We represent `shares` and NMT Merkle proofs as IPFS -// blocks and IPLD nodes so Bitswap can access those. -func (s *Store) CARBlockstore( - ctx context.Context, - root share.DataHash, -) (*BlockstoreCloser, error) { - ctx, span := tracer.Start(ctx, "store/car-blockstore") - tnow := time.Now() - cbs, err := s.carBlockstore(ctx, root) - s.metrics.observeCARBlockstore(ctx, time.Since(tnow), err != nil) - utils.SetStatusAndEnd(span, err) - return cbs, err -} - -func (s *Store) carBlockstore( - ctx context.Context, - root share.DataHash, -) (*BlockstoreCloser, error) { - key := shard.KeyFromString(root.String()) - accessor, err := s.cache.Load().Get(key) - if err == nil { - return blockstoreCloser(accessor) - } - - // if the accessor is not found in the cache, create a new one from dagstore - sa, err := s.getAccessor(ctx, key) - if err != nil { - return nil, fmt.Errorf("failed to get accessor: %w", err) - } - return blockstoreCloser(sa) -} - -// GetDAH returns the DataAvailabilityHeader for the EDS identified by DataHash. -func (s *Store) GetDAH(ctx context.Context, root share.DataHash) (*share.Root, error) { - ctx, span := tracer.Start(ctx, "store/car-dah") - tnow := time.Now() - r, err := s.getDAH(ctx, root) - s.metrics.observeGetDAH(ctx, time.Since(tnow), err != nil) - utils.SetStatusAndEnd(span, err) - return r, err -} - -func (s *Store) getDAH(ctx context.Context, root share.DataHash) (*share.Root, error) { - r, err := s.getCAR(ctx, root) - if err != nil { - return nil, fmt.Errorf("eds/store: failed to get CAR file: %w", err) - } - defer closeAndLog("car reader", r) - - carHeader, err := carv1.ReadHeader(bufio.NewReader(r)) - if err != nil { - return nil, fmt.Errorf("eds/store: failed to read car header: %w", err) - } - - dah := dahFromCARHeader(carHeader) - if !bytes.Equal(dah.Hash(), root) { - return nil, fmt.Errorf("eds/store: content integrity mismatch from CAR for root %x", root) - } - return dah, nil -} - -// dahFromCARHeader returns the DataAvailabilityHeader stored in the CIDs of a CARv1 header. -func dahFromCARHeader(carHeader *carv1.CarHeader) *share.Root { - rootCount := len(carHeader.Roots) - rootBytes := make([][]byte, 0, rootCount) - for _, root := range carHeader.Roots { - rootBytes = append(rootBytes, ipld.NamespacedSha256FromCID(root)) - } - return &share.Root{ - RowRoots: rootBytes[:rootCount/2], - ColumnRoots: rootBytes[rootCount/2:], - } -} - -func (s *Store) getAccessor(ctx context.Context, key shard.Key) (cache.Accessor, error) { - ch := make(chan dagstore.ShardResult, 1) - err := s.dgstr.AcquireShard(ctx, key, ch, dagstore.AcquireOpts{}) - if err != nil { - if errors.Is(err, dagstore.ErrShardUnknown) { - return nil, ErrNotFound - } - return nil, fmt.Errorf("failed to initialize shard acquisition: %w", err) - } - - select { - case res := <-ch: - if res.Error != nil { - return nil, fmt.Errorf("failed to acquire shard: %w", res.Error) - } - return res.Accessor, nil - case <-ctx.Done(): - go trackLateResult("get_shard", ch, s.metrics, time.Minute) - return nil, ctx.Err() - } -} - -// Remove removes EDS from Store by the given share.Root hash and cleans up all -// the indexing. -func (s *Store) Remove(ctx context.Context, root share.DataHash) error { - ctx, span := tracer.Start(ctx, "store/remove") - tnow := time.Now() - err := s.remove(ctx, root) - s.metrics.observeRemove(ctx, time.Since(tnow), err != nil) - utils.SetStatusAndEnd(span, err) - return err -} - -func (s *Store) remove(ctx context.Context, root share.DataHash) (err error) { - key := shard.KeyFromString(root.String()) - // remove open links to accessor from cache - if err := s.cache.Load().Remove(key); err != nil { - log.Warnw("remove accessor from cache", "err", err) - } - ch := make(chan dagstore.ShardResult, 1) - err = s.dgstr.DestroyShard(ctx, key, ch, dagstore.DestroyOpts{}) - if err != nil { - return fmt.Errorf("failed to initiate shard destruction: %w", err) - } - - select { - case result := <-ch: - if result.Error != nil { - return fmt.Errorf("failed to destroy shard: %w", result.Error) - } - case <-ctx.Done(): - go trackLateResult("remove", ch, s.metrics, time.Minute) - return ctx.Err() - } - - dropped, err := s.carIdx.DropFullIndex(key) - if !dropped { - log.Warnf("failed to drop index for %s", key) - } - if err != nil { - return fmt.Errorf("failed to drop index for %s: %w", key, err) - } - - err = os.Remove(s.basepath + blocksPath + root.String()) - if err != nil { - return fmt.Errorf("failed to remove CAR file: %w", err) - } - return nil -} - -// Get reads EDS out of Store by given DataRoot. -// -// It reads only one quadrant(1/4) of the EDS and verifies the integrity of the stored data by -// recomputing it. -func (s *Store) Get(ctx context.Context, root share.DataHash) (*rsmt2d.ExtendedDataSquare, error) { - ctx, span := tracer.Start(ctx, "store/get") - tnow := time.Now() - eds, err := s.get(ctx, root) - s.metrics.observeGet(ctx, time.Since(tnow), err != nil) - utils.SetStatusAndEnd(span, err) - return eds, err -} - -func (s *Store) get(ctx context.Context, root share.DataHash) (eds *rsmt2d.ExtendedDataSquare, err error) { - ctx, span := tracer.Start(ctx, "store/get") - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - r, err := s.getCAR(ctx, root) - if err != nil { - return nil, fmt.Errorf("failed to get CAR file: %w", err) - } - defer closeAndLog("car reader", r) - - eds, err = ReadEDS(ctx, r, root) - if err != nil { - return nil, fmt.Errorf("failed to read EDS from CAR file: %w", err) - } - return eds, nil -} - -// Has checks if EDS exists by the given share.Root hash. -func (s *Store) Has(ctx context.Context, root share.DataHash) (has bool, err error) { - ctx, span := tracer.Start(ctx, "store/has") - tnow := time.Now() - eds, err := s.has(ctx, root) - s.metrics.observeHas(ctx, time.Since(tnow), err != nil) - utils.SetStatusAndEnd(span, err) - return eds, err -} - -func (s *Store) has(_ context.Context, root share.DataHash) (bool, error) { - key := root.String() - info, err := s.dgstr.GetShardInfo(shard.KeyFromString(key)) - switch { - case err == nil: - return true, info.Error - case errors.Is(err, dagstore.ErrShardUnknown): - return false, info.Error - default: - return false, err - } -} - -// List lists all the registered EDSes. -func (s *Store) List() ([]share.DataHash, error) { - ctx, span := tracer.Start(context.Background(), "store/list") - tnow := time.Now() - hashes, err := s.list() - s.metrics.observeList(ctx, time.Since(tnow), err != nil) - utils.SetStatusAndEnd(span, err) - return hashes, err -} - -func (s *Store) list() ([]share.DataHash, error) { - shards := s.dgstr.AllShardsInfo() - hashes := make([]share.DataHash, 0, len(shards)) - for shrd := range shards { - hash := share.MustDataHashFromString(shrd.String()) - hashes = append(hashes, hash) - } - return hashes, nil -} - -func setupPath(basepath string) error { - err := os.MkdirAll(basepath+blocksPath, os.ModePerm) - if err != nil { - return fmt.Errorf("failed to create blocks directory: %w", err) - } - err = os.MkdirAll(basepath+transientsPath, os.ModePerm) - if err != nil { - return fmt.Errorf("failed to create transients directory: %w", err) - } - err = os.MkdirAll(basepath+indexPath, os.ModePerm) - if err != nil { - return fmt.Errorf("failed to create index directory: %w", err) - } - return nil -} - -// inMemoryOnceMount is used to allow reading once from buffer before using main mount.Reader -type inMemoryOnceMount struct { - buf *bytes.Buffer - - readOnce atomic.Bool - mount.FileMount -} - -func (m *inMemoryOnceMount) Fetch(ctx context.Context) (mount.Reader, error) { - if m.buf != nil && !m.readOnce.Swap(true) { - reader := &inMemoryReader{Reader: bytes.NewReader(m.buf.Bytes())} - // release memory for gc, otherwise buffer will stick forever - m.buf = nil - return reader, nil - } - return m.FileMount.Fetch(ctx) -} - -func (m *inMemoryOnceMount) Write(b []byte) (int, error) { - return m.buf.Write(b) -} - -func (m *inMemoryOnceMount) WriteTo(w io.Writer) (int64, error) { - return io.Copy(w, bytes.NewReader(m.buf.Bytes())) -} - -// inMemoryReader extends bytes.Reader to implement mount.Reader interface -type inMemoryReader struct { - *bytes.Reader -} - -// Close allows inMemoryReader to satisfy mount.Reader interface -func (r *inMemoryReader) Close() error { - return nil -} diff --git a/share/eds/store_options.go b/share/eds/store_options.go deleted file mode 100644 index c8dcc69136..0000000000 --- a/share/eds/store_options.go +++ /dev/null @@ -1,43 +0,0 @@ -package eds - -import ( - "errors" - "time" -) - -type Parameters struct { - // GC performs DAG store garbage collection by reclaiming transient files of - // shards that are currently available but inactive, or errored. - // We don't use transient files right now, so GC is turned off by default. - GCInterval time.Duration - - // RecentBlocksCacheSize is the size of the cache for recent blocks. - RecentBlocksCacheSize int - - // BlockstoreCacheSize is the size of the cache for blockstore requested accessors. - BlockstoreCacheSize int -} - -// DefaultParameters returns the default configuration values for the EDS store parameters. -func DefaultParameters() *Parameters { - return &Parameters{ - GCInterval: 0, - RecentBlocksCacheSize: 10, - BlockstoreCacheSize: 128, - } -} - -func (p *Parameters) Validate() error { - if p.GCInterval < 0 { - return errors.New("eds: GC interval cannot be negative") - } - - if p.RecentBlocksCacheSize < 1 { - return errors.New("eds: recent blocks cache size must be positive") - } - - if p.BlockstoreCacheSize < 1 { - return errors.New("eds: blockstore cache size must be positive") - } - return nil -} diff --git a/share/eds/store_test.go b/share/eds/store_test.go deleted file mode 100644 index c5d87f7352..0000000000 --- a/share/eds/store_test.go +++ /dev/null @@ -1,539 +0,0 @@ -package eds - -import ( - "context" - "io" - "os" - "sync" - "testing" - "time" - - "github.com/filecoin-project/dagstore" - "github.com/filecoin-project/dagstore/shard" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" - dsbadger "github.com/ipfs/go-ds-badger4" - "github.com/ipld/go-car" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds/cache" - "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -func TestEDSStore(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - edsStore, err := newStore(t) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - - // PutRegistersShard tests if Put registers the shard on the underlying DAGStore - t.Run("PutRegistersShard", func(t *testing.T) { - eds, dah := randomEDS(t) - - // shard hasn't been registered yet - has, err := edsStore.Has(ctx, dah.Hash()) - assert.False(t, has) - assert.NoError(t, err) - - err = edsStore.Put(ctx, dah.Hash(), eds) - assert.NoError(t, err) - - _, err = edsStore.dgstr.GetShardInfo(shard.KeyFromString(dah.String())) - assert.NoError(t, err) - }) - - // PutIndexesEDS ensures that Putting an EDS indexes it into the car index - t.Run("PutIndexesEDS", func(t *testing.T) { - eds, dah := randomEDS(t) - - stat, _ := edsStore.carIdx.StatFullIndex(shard.KeyFromString(dah.String())) - assert.False(t, stat.Exists) - - err = edsStore.Put(ctx, dah.Hash(), eds) - assert.NoError(t, err) - - stat, err = edsStore.carIdx.StatFullIndex(shard.KeyFromString(dah.String())) - assert.True(t, stat.Exists) - assert.NoError(t, err) - }) - - // GetCAR ensures that the reader returned from GetCAR is capable of reading the CAR header and - // ODS. - t.Run("GetCAR", func(t *testing.T) { - eds, dah := randomEDS(t) - - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - r, err := edsStore.GetCAR(ctx, dah.Hash()) - assert.NoError(t, err) - defer func() { - require.NoError(t, r.Close()) - }() - carReader, err := car.NewCarReader(r) - assert.NoError(t, err) - - for i := 0; i < 4; i++ { - for j := 0; j < 4; j++ { - original := eds.GetCell(uint(i), uint(j)) - block, err := carReader.Next() - assert.NoError(t, err) - assert.Equal(t, original, share.GetData(block.RawData())) - } - } - }) - - t.Run("item not exist", func(t *testing.T) { - root := share.DataHash{1} - _, err := edsStore.GetCAR(ctx, root) - assert.ErrorIs(t, err, ErrNotFound) - - _, err = edsStore.GetDAH(ctx, root) - assert.ErrorIs(t, err, ErrNotFound) - - _, err = edsStore.CARBlockstore(ctx, root) - assert.ErrorIs(t, err, ErrNotFound) - }) - - t.Run("Remove", func(t *testing.T) { - eds, dah := randomEDS(t) - - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // assert that file now exists - _, err = os.Stat(edsStore.basepath + blocksPath + dah.String()) - assert.NoError(t, err) - - // accessor will be registered in cache async on put, so give it some time to settle - time.Sleep(time.Millisecond * 100) - - err = edsStore.Remove(ctx, dah.Hash()) - assert.NoError(t, err) - - // shard should no longer be registered on the dagstore - _, err = edsStore.dgstr.GetShardInfo(shard.KeyFromString(dah.String())) - assert.Error(t, err, "shard not found") - - // shard should have been dropped from the index, which also removes the file under /index/ - indexStat, err := edsStore.carIdx.StatFullIndex(shard.KeyFromString(dah.String())) - assert.NoError(t, err) - assert.False(t, indexStat.Exists) - - // file no longer exists - _, err = os.Stat(edsStore.basepath + blocksPath + dah.String()) - assert.ErrorContains(t, err, "no such file or directory") - }) - - t.Run("Remove after OpShardFail", func(t *testing.T) { - eds, dah := randomEDS(t) - - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // assert that shard now exists - ok, err := edsStore.Has(ctx, dah.Hash()) - assert.NoError(t, err) - assert.True(t, ok) - - // assert that file now exists - path := edsStore.basepath + blocksPath + dah.String() - _, err = os.Stat(path) - assert.NoError(t, err) - - err = os.Remove(path) - assert.NoError(t, err) - - // accessor will be registered in cache async on put, so give it some time to settle - time.Sleep(time.Millisecond * 100) - - // remove non-failed accessor from cache - err = edsStore.cache.Load().Remove(shard.KeyFromString(dah.String())) - assert.NoError(t, err) - - _, err = edsStore.GetCAR(ctx, dah.Hash()) - assert.Error(t, err) - - ticker := time.NewTicker(time.Millisecond * 100) - defer ticker.Stop() - for { - select { - case <-ticker.C: - has, err := edsStore.Has(ctx, dah.Hash()) - if err == nil && !has { - // shard no longer exists after OpShardFail was detected from GetCAR call - return - } - case <-ctx.Done(): - t.Fatal("timeout waiting for shard to be removed") - } - } - }) - - t.Run("Has", func(t *testing.T) { - eds, dah := randomEDS(t) - - ok, err := edsStore.Has(ctx, dah.Hash()) - assert.NoError(t, err) - assert.False(t, ok) - - err = edsStore.Put(ctx, dah.Hash(), eds) - assert.NoError(t, err) - - ok, err = edsStore.Has(ctx, dah.Hash()) - assert.NoError(t, err) - assert.True(t, ok) - }) - - t.Run("RecentBlocksCache", func(t *testing.T) { - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // accessor will be registered in cache async on put, so give it some time to settle - time.Sleep(time.Millisecond * 100) - - // check, that the key is in the cache after put - shardKey := shard.KeyFromString(dah.String()) - _, err = edsStore.cache.Load().Get(shardKey) - assert.NoError(t, err) - }) - - t.Run("List", func(t *testing.T) { - const amount = 10 - hashes := make([]share.DataHash, 0, amount) - for range make([]byte, amount) { - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - hashes = append(hashes, dah.Hash()) - } - - hashesOut, err := edsStore.List() - require.NoError(t, err) - for _, hash := range hashes { - assert.Contains(t, hashesOut, hash) - } - }) - - t.Run("Parallel put", func(t *testing.T) { - const amount = 20 - eds, dah := randomEDS(t) - - wg := sync.WaitGroup{} - for i := 1; i < amount; i++ { - wg.Add(1) - go func() { - defer wg.Done() - err := edsStore.Put(ctx, dah.Hash(), eds) - if err != nil { - require.ErrorIs(t, err, dagstore.ErrShardExists) - } - }() - } - wg.Wait() - - eds, err := edsStore.Get(ctx, dah.Hash()) - require.NoError(t, err) - newDah, err := da.NewDataAvailabilityHeader(eds) - require.NoError(t, err) - require.Equal(t, dah.Hash(), newDah.Hash()) - }) -} - -// TestEDSStore_GC verifies that unused transient shards are collected by the GC periodically. -func TestEDSStore_GC(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - edsStore, err := newStore(t) - edsStore.gcInterval = time.Second - require.NoError(t, err) - - // kicks off the gc goroutine - err = edsStore.Start(ctx) - require.NoError(t, err) - - eds, dah := randomEDS(t) - shardKey := shard.KeyFromString(dah.String()) - - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // accessor will be registered in cache async on put, so give it some time to settle - time.Sleep(time.Millisecond * 100) - - // remove links to the shard from cache - time.Sleep(time.Millisecond * 100) - key := shard.KeyFromString(share.DataHash(dah.Hash()).String()) - err = edsStore.cache.Load().Remove(key) - require.NoError(t, err) - - // doesn't exist yet - assert.NotContains(t, edsStore.lastGCResult.Load().Shards, shardKey) - - // wait for gc to run, retry three times - for i := 0; i < 3; i++ { - time.Sleep(edsStore.gcInterval) - if _, ok := edsStore.lastGCResult.Load().Shards[shardKey]; ok { - break - } - } - assert.Contains(t, edsStore.lastGCResult.Load().Shards, shardKey) - - // assert nil in this context means there was no error re-acquiring the shard during GC - assert.Nil(t, edsStore.lastGCResult.Load().Shards[shardKey]) -} - -func Test_BlockstoreCache(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - edsStore, err := newStore(t) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - - // store eds to the store with noopCache to allow clean cache after put - swap := edsStore.cache.Load() - edsStore.cache.Store(cache.NewDoubleCache(cache.NoopCache{}, cache.NoopCache{})) - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // get any key from saved eds - bs, err := edsStore.carBlockstore(ctx, dah.Hash()) - require.NoError(t, err) - defer func() { - require.NoError(t, bs.Close()) - }() - keys, err := bs.AllKeysChan(ctx) - require.NoError(t, err) - var key cid.Cid - select { - case key = <-keys: - case <-ctx.Done(): - t.Fatal("context timeout") - } - - // swap back original cache - edsStore.cache.Store(swap) - - // key shouldn't be in cache yet, check for returned errCacheMiss - shardKey := shard.KeyFromString(dah.String()) - _, err = edsStore.cache.Load().Get(shardKey) - require.Error(t, err) - - // now get it from blockstore, to trigger storing to cache - _, err = edsStore.Blockstore().Get(ctx, key) - require.NoError(t, err) - - // should be no errCacheMiss anymore - _, err = edsStore.cache.Load().Get(shardKey) - require.NoError(t, err) -} - -// Test_CachedAccessor verifies that the reader represented by a cached accessor can be read from -// multiple times, without exhausting the underlying reader. -func Test_CachedAccessor(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - edsStore, err := newStore(t) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // accessor will be registered in cache async on put, so give it some time to settle - time.Sleep(time.Millisecond * 100) - - // accessor should be in cache - _, err = edsStore.cache.Load().Get(shard.KeyFromString(dah.String())) - require.NoError(t, err) - - // first read from cached accessor - carReader, err := edsStore.getCAR(ctx, dah.Hash()) - require.NoError(t, err) - firstBlock, err := io.ReadAll(carReader) - require.NoError(t, err) - require.NoError(t, carReader.Close()) - - // second read from cached accessor - carReader, err = edsStore.getCAR(ctx, dah.Hash()) - require.NoError(t, err) - secondBlock, err := io.ReadAll(carReader) - require.NoError(t, err) - require.NoError(t, carReader.Close()) - - require.Equal(t, firstBlock, secondBlock) -} - -// Test_CachedAccessor verifies that the reader represented by a accessor obtained directly from -// dagstore can be read from multiple times, without exhausting the underlying reader. -func Test_NotCachedAccessor(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - edsStore, err := newStore(t) - require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) - // replace cache with noopCache to - edsStore.cache.Store(cache.NewDoubleCache(cache.NoopCache{}, cache.NoopCache{})) - - eds, dah := randomEDS(t) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(t, err) - - // accessor will be registered in cache async on put, so give it some time to settle - time.Sleep(time.Millisecond * 100) - - // accessor should not be in cache - _, err = edsStore.cache.Load().Get(shard.KeyFromString(dah.String())) - require.Error(t, err) - - // first read from direct accessor (not from cache) - carReader, err := edsStore.getCAR(ctx, dah.Hash()) - require.NoError(t, err) - firstBlock, err := io.ReadAll(carReader) - require.NoError(t, err) - require.NoError(t, carReader.Close()) - - // second read from direct accessor (not from cache) - carReader, err = edsStore.getCAR(ctx, dah.Hash()) - require.NoError(t, err) - secondBlock, err := io.ReadAll(carReader) - require.NoError(t, err) - require.NoError(t, carReader.Close()) - - require.Equal(t, firstBlock, secondBlock) -} - -func BenchmarkStore(b *testing.B) { - ctx, cancel := context.WithCancel(context.Background()) - b.Cleanup(cancel) - - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - edsStore, err := NewStore(DefaultParameters(), b.TempDir(), ds) - require.NoError(b, err) - err = edsStore.Start(ctx) - require.NoError(b, err) - - // BenchmarkStore/bench_put_128-10 10 3231859283 ns/op (~3sec) - b.Run("bench put 128", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - // pause the timer for initializing test data - b.StopTimer() - eds := edstest.RandEDS(b, 128) - dah, err := share.NewRoot(eds) - require.NoError(b, err) - b.StartTimer() - - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(b, err) - } - }) - - // BenchmarkStore/bench_read_128-10 14 78970661 ns/op (~70ms) - b.Run("bench read 128", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - // pause the timer for initializing test data - b.StopTimer() - eds := edstest.RandEDS(b, 128) - dah, err := share.NewRoot(eds) - require.NoError(b, err) - _ = edsStore.Put(ctx, dah.Hash(), eds) - b.StartTimer() - - _, err = edsStore.Get(ctx, dah.Hash()) - require.NoError(b, err) - } - }) -} - -// BenchmarkCacheEviction benchmarks the time it takes to load a block to the cache, when the -// cache size is set to 1. This forces cache eviction on every read. -// BenchmarkCacheEviction-10/128 384 3533586 ns/op (~3ms) -func BenchmarkCacheEviction(b *testing.B) { - const ( - blocks = 4 - size = 128 - ) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - b.Cleanup(cancel) - - dir := b.TempDir() - ds, err := dsbadger.NewDatastore(dir, &dsbadger.DefaultOptions) - require.NoError(b, err) - - newStore := func(params *Parameters) *Store { - edsStore, err := NewStore(params, dir, ds) - require.NoError(b, err) - err = edsStore.Start(ctx) - require.NoError(b, err) - return edsStore - } - edsStore := newStore(DefaultParameters()) - - // generate EDSs and store them - cids := make([]cid.Cid, blocks) - for i := range cids { - eds := edstest.RandEDS(b, size) - dah, err := da.NewDataAvailabilityHeader(eds) - require.NoError(b, err) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(b, err) - - // store cids for read loop later - cids[i] = ipld.MustCidFromNamespacedSha256(dah.RowRoots[0]) - } - - // restart store to clear cache - require.NoError(b, edsStore.Stop(ctx)) - - // set BlockstoreCacheSize to 1 to force eviction on every read - params := DefaultParameters() - params.BlockstoreCacheSize = 1 - bstore := newStore(params).Blockstore() - - // start benchmark - b.ResetTimer() - for i := 0; i < b.N; i++ { - h := cids[i%blocks] - // every read will trigger eviction - _, err := bstore.Get(ctx, h) - require.NoError(b, err) - } -} - -func newStore(t *testing.T) (*Store, error) { - t.Helper() - - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - return NewStore(DefaultParameters(), t.TempDir(), ds) -} - -func randomEDS(t *testing.T) (*rsmt2d.ExtendedDataSquare, *share.Root) { - eds := edstest.RandEDS(t, 4) - dah, err := share.NewRoot(eds) - require.NoError(t, err) - - return eds, dah -} diff --git a/share/eds/testdata/README.md b/share/eds/testdata/README.md deleted file mode 100644 index 960549e2a0..0000000000 --- a/share/eds/testdata/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# CARxEDS Testdata - -This directory contains an example CARv1 file of an EDS and its matching data availability header. - -They might need to be regenerated when modifying constants such as the default share size. This can be done by running the test utility in `eds_test.go` called `createTestData`. diff --git a/share/eds/testdata/example-root.json b/share/eds/testdata/example-root.json deleted file mode 100644 index 999d6301b6..0000000000 --- a/share/eds/testdata/example-root.json +++ /dev/null @@ -1,22 +0,0 @@ -{ -"row_roots": [ -"AAAAAAAAAAAAAAAAAAAAAAAAABPYEuDlO9Dz69oAAAAAAAAAAAAAAAAAAAAAAAAAMcklN0h38T4b/UBC/Cmr5YWmjmmxvi1e35vZBW14b8gDHBoTFVvY6H4J", -"AAAAAAAAAAAAAAAAAAAAAAAAADxyZecUZD41W5IAAAAAAAAAAAAAAAAAAAAAAAAAh8vQUZ38PaWyeUs7dQhphIuRIKiGaTr4KFwEhMRhejTd6/4NHdnKTDyY", -"AAAAAAAAAAAAAAAAAAAAAAAAAKDQatbQSwQ9uJsAAAAAAAAAAAAAAAAAAAAAAAAArtdqXCSsM1OlVCRZqqfZDnEO9eC5cwlgy5MQHb2g4NLr7nZYTruiOoz7", -"AAAAAAAAAAAAAAAAAAAAAAAAAMeUhM8LZBo9sWwAAAAAAAAAAAAAAAAAAAAAAAAA8PtvJpbDc4APKOK6MT1k61HuQXwauWw3nFWwr9pSljiYMv6jjjdLDF8o", -"/////////////////////////////////////////////////////////////////////////////xnHmhDh4Y8vfJrgewAcvLWpvI5XOyATj1IQDkCwvIEh", -"/////////////////////////////////////////////////////////////////////////////+qngp0AfoykfXwsMBukRtYxNA/bzW0+F3J7Q/+S1YZJ", -"/////////////////////////////////////////////////////////////////////////////4WNPrME/2MLrIZgAUoKaVx2GzJqDcYGrBg+sudPKUDy", -"/////////////////////////////////////////////////////////////////////////////6HdebpaHl7iTpLvmuPvtQNnkHfNOPyEhahxbVnIB2d1" -], -"column_roots": [ -"AAAAAAAAAAAAAAAAAAAAAAAAABPYEuDlO9Dz69oAAAAAAAAAAAAAAAAAAAAAAAAAx5SEzwtkGj2xbESyOeamsjGWUBQdAQoiSl+rMtNMo1wEtfGQnFS/g+K+", -"AAAAAAAAAAAAAAAAAAAAAAAAAC3uK6nhCxHTfBwAAAAAAAAAAAAAAAAAAAAAAAAA1fxnqHyO6qV39pcUQ8MuTfJ7RBhbSVWf0aamUP27KRY0II55oJoY6Ng6", -"AAAAAAAAAAAAAAAAAAAAAAAAAC6DkYeeBY/kKvAAAAAAAAAAAAAAAAAAAAAAAAAA47rxk8hoCnWGM+CX47TlYWBeE2unvRhA/j3EvHdxeL1rFRkaYfAd5eg7", -"AAAAAAAAAAAAAAAAAAAAAAAAADHJJTdId/E+G/0AAAAAAAAAAAAAAAAAAAAAAAAA8PtvJpbDc4APKAk5QPSH59HECE2sf/CDLKAZJjWo9DD4sLXJQ4jTZoH6", -"/////////////////////////////////////////////////////////////////////////////4lKCT3K11RnNIuLNfY+SfDZCYAE2iW0hjQHIVBpoN0q", -"/////////////////////////////////////////////////////////////////////////////1NpYcgayEVenbFeEO5LJ1j1/1sD+PvZWHDv+jqT1dLR", -"/////////////////////////////////////////////////////////////////////////////8FOWVuCU0rTzUW9tP2R47RmTBvwXX8ycKrMhgKEi1xa", -"/////////////////////////////////////////////////////////////////////////////7K5SoZ3HF5QgPvIXpKSr9eT4Xfiokc3PUMmXE4pBDTf" -] -} \ No newline at end of file diff --git a/share/eds/testdata/example.car b/share/eds/testdata/example.car deleted file mode 100644 index 4d33c0ef33..0000000000 Binary files a/share/eds/testdata/example.car and /dev/null differ diff --git a/share/eds/testing.go b/share/eds/testing.go new file mode 100644 index 0000000000..c8859dca5a --- /dev/null +++ b/share/eds/testing.go @@ -0,0 +1,486 @@ +package eds + +import ( + "context" + "fmt" + "math/rand/v2" + "strconv" + "sync" + "testing" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/nmt" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +type ( + createAccessor func(testing.TB, *rsmt2d.ExtendedDataSquare) Accessor + createAccessorStreamer func(testing.TB, *rsmt2d.ExtendedDataSquare) AccessorStreamer +) + +// TestSuiteAccessor runs a suite of tests for the given Accessor implementation. +func TestSuiteAccessor( + ctx context.Context, + t *testing.T, + createAccessor createAccessor, + maxSize int, +) { + minSize := 2 + if !checkPowerOfTwo(maxSize) { + t.Errorf("minSize must be power of 2: %v", maxSize) + } + for size := minSize; size <= maxSize; size *= 2 { + for name, eds := range testEDSes(t, size) { + t.Run(fmt.Sprintf("DataHash:%s", name), func(t *testing.T) { + t.Parallel() + testAccessorDataHash(ctx, t, createAccessor, eds) + }) + + t.Run(fmt.Sprintf("AxisRoots:%s", name), func(t *testing.T) { + t.Parallel() + testAccessorAxisRoots(ctx, t, createAccessor, eds) + }) + + t.Run(fmt.Sprintf("Sample:%s", name), func(t *testing.T) { + t.Parallel() + testAccessorSample(ctx, t, createAccessor, eds) + }) + + t.Run(fmt.Sprintf("AxisHalf:%s", name), func(t *testing.T) { + t.Parallel() + testAccessorAxisHalf(ctx, t, createAccessor, eds) + }) + + t.Run(fmt.Sprintf("RowNamespaceData:%s", name), func(t *testing.T) { + t.Parallel() + testAccessorRowNamespaceData(ctx, t, createAccessor, size) + }) + + t.Run(fmt.Sprintf("Shares:%s", name), func(t *testing.T) { + t.Parallel() + testAccessorShares(ctx, t, createAccessor, eds) + }) + } + } +} + +func testEDSes(t *testing.T, sizes ...int) map[string]*rsmt2d.ExtendedDataSquare { + testEDSes := make(map[string]*rsmt2d.ExtendedDataSquare) + for _, size := range sizes { + fullEDS := edstest.RandEDS(t, size) + testEDSes[fmt.Sprintf("FullODS:%d", size)] = fullEDS + + var padding int + for padding < 1 { + padding = rand.IntN(size * size) //nolint:gosec + } + paddingEds := edstest.RandEDSWithTailPadding(t, size, padding) + testEDSes[fmt.Sprintf("PaddedODS:%d", size)] = paddingEds + } + + testEDSes["EmptyODS"] = share.EmptyEDS() + return testEDSes +} + +func TestStreamer( + ctx context.Context, + t *testing.T, + create createAccessorStreamer, + odsSize int, +) { + for name, eds := range testEDSes(t, odsSize) { + t.Run(fmt.Sprintf("Reader:%s", name), func(t *testing.T) { + t.Parallel() + testAccessorReader(ctx, t, create, eds) + }) + } +} + +func testAccessorDataHash( + ctx context.Context, + t *testing.T, + createAccessor createAccessor, + eds *rsmt2d.ExtendedDataSquare, +) { + acc := createAccessor(t, eds) + + expected, err := share.NewAxisRoots(eds) + require.NoError(t, err) + + datahash, err := acc.DataHash(ctx) + require.NoError(t, err) + require.Equal(t, share.DataHash(expected.Hash()), datahash) +} + +func testAccessorAxisRoots( + ctx context.Context, + t *testing.T, + createAccessor createAccessor, + eds *rsmt2d.ExtendedDataSquare, +) { + acc := createAccessor(t, eds) + + expected, err := share.NewAxisRoots(eds) + require.NoError(t, err) + + roots, err := acc.AxisRoots(ctx) + require.NoError(t, err) + require.True(t, expected.Equals(roots)) +} + +func testAccessorSample( + ctx context.Context, + t *testing.T, + createAccessor createAccessor, + eds *rsmt2d.ExtendedDataSquare, +) { + width := int(eds.Width()) + t.Run("single thread", func(t *testing.T) { + acc := createAccessor(t, eds) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + // t.Parallel() this fails the test for some reason + for rowIdx := 0; rowIdx < width; rowIdx++ { + for colIdx := 0; colIdx < width; colIdx++ { + idx := shwap.SampleCoords{Row: rowIdx, Col: colIdx} + testSample(ctx, t, acc, roots, idx) + } + } + }) + + t.Run("parallel", func(t *testing.T) { + t.Parallel() + acc := createAccessor(t, eds) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + wg := sync.WaitGroup{} + for rowIdx := 0; rowIdx < width; rowIdx++ { + for colIdx := 0; colIdx < width; colIdx++ { + wg.Add(1) + idx := shwap.SampleCoords{Row: rowIdx, Col: colIdx} + go func(idx shwap.SampleCoords) { + defer wg.Done() + testSample(ctx, t, acc, roots, idx) + }(idx) + } + } + wg.Wait() + }) + + t.Run("random", func(t *testing.T) { + t.Parallel() + acc := createAccessor(t, eds) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + + wg := sync.WaitGroup{} + for range 1000 { + wg.Add(1) + go func() { + defer wg.Done() + rowIdx := rand.IntN(int(eds.Width())) //nolint:gosec + colIdx := rand.IntN(int(eds.Width())) //nolint:gosec + testSample(ctx, t, acc, roots, shwap.SampleCoords{Row: rowIdx, Col: colIdx}) + }() + } + wg.Wait() + }) +} + +func testSample( + ctx context.Context, + t *testing.T, + acc Accessor, + roots *share.AxisRoots, + idx shwap.SampleCoords, +) { + shr, err := acc.Sample(ctx, idx) + require.NoError(t, err) + + err = shr.Verify(roots, idx.Row, idx.Col) + require.NoError(t, err) +} + +func testAccessorRowNamespaceData( + ctx context.Context, + t *testing.T, + createAccessor createAccessor, + odsSize int, +) { + t.Run("included", func(t *testing.T) { + t.Parallel() + // generate EDS with random data and some Shares with the same namespace + sharesAmount := odsSize * odsSize + namespace := libshare.RandomNamespace() + // test with different amount of shares + for amount := 1; amount < sharesAmount; amount++ { + // select random amount of shares, but not less than 1 + eds, roots := edstest.RandEDSWithNamespace(t, namespace, amount, odsSize) + acc := createAccessor(t, eds) + + var actualSharesAmount int + // loop over all rows and check that the amount of shares in the namespace is equal to the expected + // amount + for i, root := range roots.RowRoots { + rowData, err := acc.RowNamespaceData(ctx, namespace, i) + + // namespace is not included in the row, so there should be no shares + outside, outsideErr := share.IsOutsideRange(namespace, root, root) + require.NoError(t, outsideErr) + if outside { + require.ErrorIs(t, err, shwap.ErrNamespaceOutsideRange) + require.Len(t, rowData.Shares, 0) + continue + } + + actualSharesAmount += len(rowData.Shares) + require.NoError(t, err) + require.True(t, len(rowData.Shares) > 0) + err = rowData.Verify(roots, namespace, i) + require.NoError(t, err) + } + + // check that the amount of shares in the namespace is equal to the expected amount + require.Equal(t, amount, actualSharesAmount) + } + }) + + t.Run("not included", func(t *testing.T) { + t.Parallel() + // generate EDS with random data and some Shares with the same namespace + eds := edstest.RandEDS(t, odsSize) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + + // loop over first half of the rows, because the second half is parity and does not contain + // namespaced shares + for i, root := range roots.RowRoots[:odsSize] { + // select namespace that within the range of root namespaces, but is not included + maxNs := nmt.MaxNamespace(root, libshare.NamespaceSize) + ns, err := libshare.NewNamespaceFromBytes(maxNs) + require.NoError(t, err) + + absentNs, err := ns.AddInt(-1) + require.NoError(t, err) + + acc := createAccessor(t, eds) + rowData, err := acc.RowNamespaceData(ctx, absentNs, i) + require.NoError(t, err) + + // namespace is not included in the row, so there should be no shares + require.Len(t, rowData.Shares, 0) + require.True(t, rowData.Proof.IsOfAbsence()) + + err = rowData.Verify(roots, absentNs, i) + require.NoError(t, err) + } + }) +} + +func testAccessorAxisHalf( + ctx context.Context, + t *testing.T, + createAccessor createAccessor, + eds *rsmt2d.ExtendedDataSquare, +) { + odsSize := int(eds.Width() / 2) + acc := createAccessor(t, eds) + + t.Run("single thread", func(t *testing.T) { + for _, axisType := range []rsmt2d.Axis{rsmt2d.Col, rsmt2d.Row} { + for axisIdx := 0; axisIdx < int(eds.Width()); axisIdx++ { + half, err := acc.AxisHalf(ctx, axisType, axisIdx) + require.NoError(t, err) + require.Len(t, half.Shares, odsSize) + + var expected []libshare.Share + if half.IsParity { + expected, err = getAxis(eds, axisType, axisIdx) + require.NoError(t, err) + expected = expected[odsSize:] + } else { + expected, err = getAxis(eds, axisType, axisIdx) + require.NoError(t, err) + expected = expected[:odsSize] + } + + require.Equal(t, expected, half.Shares) + } + } + }) + + t.Run("parallel", func(t *testing.T) { + t.Parallel() + wg := sync.WaitGroup{} + for _, axisType := range []rsmt2d.Axis{rsmt2d.Col, rsmt2d.Row} { + for i := 0; i < int(eds.Width()); i++ { + wg.Add(1) + go func(axisType rsmt2d.Axis, idx int) { + defer wg.Done() + half, err := acc.AxisHalf(ctx, axisType, idx) + require.NoError(t, err) + require.Len(t, half.Shares, odsSize) + + var expected []libshare.Share + if half.IsParity { + expected, err = getAxis(eds, axisType, idx) + require.NoError(t, err) + expected = expected[odsSize:] + } else { + expected, err = getAxis(eds, axisType, idx) + require.NoError(t, err) + expected = expected[:odsSize] + } + require.Equal(t, expected, half.Shares) + }(axisType, i) + } + } + wg.Wait() + }) +} + +func testAccessorShares( + ctx context.Context, + t *testing.T, + createAccessor createAccessor, + eds *rsmt2d.ExtendedDataSquare, +) { + acc := createAccessor(t, eds) + + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + shares, err := acc.Shares(ctx) + require.NoError(t, err) + expected := eds.FlattenedODS() + sh, err := libshare.FromBytes(expected) + require.NoError(t, err) + require.Equal(t, sh, shares) + }() + } + wg.Wait() +} + +func testAccessorReader( + ctx context.Context, + t *testing.T, + createAccessor createAccessorStreamer, + eds *rsmt2d.ExtendedDataSquare, +) { + acc := createAccessor(t, eds) + + // verify that the reader represented by accessor can be read from + // multiple times, without exhausting the underlying reader. + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + testReader(ctx, t, eds, acc) + }() + } + wg.Wait() +} + +func testReader(ctx context.Context, t *testing.T, eds *rsmt2d.ExtendedDataSquare, as AccessorStreamer) { + reader, err := as.Reader() + require.NoError(t, err) + + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + + actual, err := ReadAccessor(ctx, reader, roots) + require.NoError(t, err) + require.True(t, eds.Equals(actual.ExtendedDataSquare)) +} + +func BenchGetHalfAxisFromAccessor( + ctx context.Context, + b *testing.B, + createAccessor createAccessor, + minOdsSize, maxOdsSize int, +) { + for size := minOdsSize; size <= maxOdsSize; size *= 2 { + eds := edstest.RandEDS(b, size) + acc := createAccessor(b, eds) + + // loop over all possible axis types and quadrants + for _, axisType := range []rsmt2d.Axis{rsmt2d.Row, rsmt2d.Col} { + for _, squareHalf := range []int{0, 1} { + name := fmt.Sprintf("Size:%v/ProofType:%s/squareHalf:%s", size, axisType, strconv.Itoa(squareHalf)) + b.Run(name, func(b *testing.B) { + // warm up cache + _, err := acc.AxisHalf(ctx, axisType, acc.Size(ctx)/2*(squareHalf)) + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := acc.AxisHalf(ctx, axisType, acc.Size(ctx)/2*(squareHalf)) + require.NoError(b, err) + } + }) + } + } + } +} + +func BenchGetSampleFromAccessor( + ctx context.Context, + b *testing.B, + createAccessor createAccessor, + minOdsSize, maxOdsSize int, +) { + for size := minOdsSize; size <= maxOdsSize; size *= 2 { + eds := edstest.RandEDS(b, size) + acc := createAccessor(b, eds) + + // loop over all possible axis types and quadrants + for _, q := range quadrants { + name := fmt.Sprintf("Size:%v/quadrant:%s", size, q) + b.Run(name, func(b *testing.B) { + rowIdx, colIdx := q.coordinates(acc.Size(ctx)) + idx := shwap.SampleCoords{Row: rowIdx, Col: colIdx} + + // warm up cache + _, err := acc.Sample(ctx, idx) + require.NoError(b, err, q.String()) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := acc.Sample(ctx, idx) + require.NoError(b, err) + } + }) + } + } +} + +type quadrantIdx int + +var quadrants = []quadrantIdx{1, 2, 3, 4} + +func (q quadrantIdx) String() string { + return strconv.Itoa(int(q)) +} + +func (q quadrantIdx) coordinates(edsSize int) (rowIdx, colIdx int) { + colIdx = edsSize/2*(int(q-1)%2) + 1 + rowIdx = edsSize/2*(int(q-1)/2) + 1 + return rowIdx, colIdx +} + +func checkPowerOfTwo(n int) bool { + // added one corner case if n is zero it will also consider as power 2 + if n == 0 { + return true + } + return n&(n-1) == 0 +} diff --git a/share/eds/utils.go b/share/eds/utils.go deleted file mode 100644 index fd152e246f..0000000000 --- a/share/eds/utils.go +++ /dev/null @@ -1,150 +0,0 @@ -package eds - -import ( - "context" - "errors" - "fmt" - "io" - - "github.com/filecoin-project/dagstore" - "github.com/ipfs/boxo/blockservice" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - "golang.org/x/sync/errgroup" - - "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds/cache" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -// readCloser is a helper struct, that combines io.Reader and io.Closer -type readCloser struct { - io.Reader - io.Closer -} - -// BlockstoreCloser represents a blockstore that can also be closed. It combines the functionality -// of a dagstore.ReadBlockstore with that of an io.Closer. -type BlockstoreCloser struct { - dagstore.ReadBlockstore - io.Closer -} - -func newReadCloser(ac cache.Accessor) io.ReadCloser { - return readCloser{ - ac.Reader(), - ac, - } -} - -// blockstoreCloser constructs new BlockstoreCloser from cache.Accessor -func blockstoreCloser(ac cache.Accessor) (*BlockstoreCloser, error) { - bs, err := ac.Blockstore() - if err != nil { - return nil, fmt.Errorf("eds/store: failed to get blockstore: %w", err) - } - return &BlockstoreCloser{ - ReadBlockstore: bs, - Closer: ac, - }, nil -} - -func closeAndLog(name string, closer io.Closer) { - if err := closer.Close(); err != nil { - log.Warnw("closing "+name, "err", err) - } -} - -// RetrieveNamespaceFromStore gets all EDS shares in the given namespace from -// the EDS store through the corresponding CAR-level blockstore. It is extracted -// from the store getter to make it available for reuse in the shrexnd server. -func RetrieveNamespaceFromStore( - ctx context.Context, - store *Store, - dah *share.Root, - namespace share.Namespace, -) (shares share.NamespacedShares, err error) { - if err = namespace.ValidateForData(); err != nil { - return nil, err - } - - bs, err := store.CARBlockstore(ctx, dah.Hash()) - if errors.Is(err, ErrNotFound) { - // convert error to satisfy getter interface contract - err = share.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("failed to retrieve blockstore from eds store: %w", err) - } - defer func() { - if err := bs.Close(); err != nil { - log.Warnw("closing blockstore", "err", err) - } - }() - - // wrap the read-only CAR blockstore in a getter - blockGetter := NewBlockGetter(bs) - shares, err = CollectSharesByNamespace(ctx, blockGetter, dah, namespace) - if errors.Is(err, ipld.ErrNodeNotFound) { - // IPLD node not found after the index pointed to this shard and the CAR - // blockstore has been opened successfully is a strong indicator of - // corruption. We remove the block on bridges and fulls and return - // share.ErrNotFound to ensure the data is retrieved by the next getter. - // Note that this recovery is manual and will only be restored by an RPC - // call to SharesAvailable that fetches the same datahash that was - // removed. - err = store.Remove(ctx, dah.Hash()) - if err != nil { - log.Errorf("failed to remove CAR from store after detected corruption: %w", err) - } - err = share.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("failed to retrieve shares by namespace from store: %w", err) - } - - return shares, nil -} - -// CollectSharesByNamespace collects NamespaceShares within the given namespace from share.Root. -func CollectSharesByNamespace( - ctx context.Context, - bg blockservice.BlockGetter, - root *share.Root, - namespace share.Namespace, -) (shares share.NamespacedShares, err error) { - ctx, span := tracer.Start(ctx, "collect-shares-by-namespace", trace.WithAttributes( - attribute.String("namespace", namespace.String()), - )) - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - rootCIDs := ipld.FilterRootByNamespace(root, namespace) - if len(rootCIDs) == 0 { - return []share.NamespacedRow{}, nil - } - - errGroup, ctx := errgroup.WithContext(ctx) - shares = make([]share.NamespacedRow, len(rootCIDs)) - for i, rootCID := range rootCIDs { - errGroup.Go(func() error { - row, proof, err := ipld.GetSharesByNamespace(ctx, bg, rootCID, namespace, len(root.RowRoots)) - shares[i] = share.NamespacedRow{ - Shares: row, - Proof: proof, - } - if err != nil { - return fmt.Errorf("retrieving shares by namespace %s for row %x: %w", namespace.String(), rootCID, err) - } - return nil - }) - } - - if err := errGroup.Wait(); err != nil { - return nil, err - } - - return shares, nil -} diff --git a/share/eds/validation.go b/share/eds/validation.go new file mode 100644 index 0000000000..4f6cf0aa85 --- /dev/null +++ b/share/eds/validation.go @@ -0,0 +1,63 @@ +package eds + +import ( + "context" + "fmt" + "sync/atomic" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ Accessor = validation{} + +// validation is a Accessor implementation that performs sanity checks on methods. It wraps +// another Accessor and performs bounds checks on index arguments. +type validation struct { + Accessor + size *atomic.Int32 +} + +func WithValidation(f Accessor) Accessor { + return &validation{Accessor: f, size: new(atomic.Int32)} +} + +func (f validation) Size(ctx context.Context) int { + size := f.size.Load() + if size == 0 { + loaded := f.Accessor.Size(ctx) + f.size.Store(int32(loaded)) + return loaded + } + return int(size) +} + +func (f validation) Sample(ctx context.Context, idx shwap.SampleCoords) (shwap.Sample, error) { + _, err := shwap.NewSampleID(1, idx, f.Size(ctx)) + if err != nil { + return shwap.Sample{}, fmt.Errorf("sample validation: %w", err) + } + return f.Accessor.Sample(ctx, idx) +} + +func (f validation) AxisHalf(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) (AxisHalf, error) { + _, err := shwap.NewRowID(1, axisIdx, f.Size(ctx)) + if err != nil { + return AxisHalf{}, fmt.Errorf("axis half validation: %w", err) + } + return f.Accessor.AxisHalf(ctx, axisType, axisIdx) +} + +func (f validation) RowNamespaceData( + ctx context.Context, + namespace libshare.Namespace, + rowIdx int, +) (shwap.RowNamespaceData, error) { + _, err := shwap.NewRowNamespaceDataID(1, rowIdx, namespace, f.Size(ctx)) + if err != nil { + return shwap.RowNamespaceData{}, fmt.Errorf("row namespace data validation: %w", err) + } + return f.Accessor.RowNamespaceData(ctx, namespace, rowIdx) +} diff --git a/share/eds/validation_test.go b/share/eds/validation_test.go new file mode 100644 index 0000000000..9ec6b3fdb3 --- /dev/null +++ b/share/eds/validation_test.go @@ -0,0 +1,105 @@ +package eds + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestValidation_Sample(t *testing.T) { + tests := []struct { + name string + rowIdx, colIdx int + odsSize int + expectFail bool + }{ + {"ValidIndices", 3, 2, 4, false}, + {"OutOfBoundsX", 8, 3, 4, true}, + {"OutOfBoundsY", 3, 8, 4, true}, + {"NegativeX", -1, 4, 8, true}, + {"NegativeY", 3, -1, 8, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + randEDS := edstest.RandEDS(t, tt.odsSize) + accessor := &Rsmt2D{ExtendedDataSquare: randEDS} + validation := WithValidation(AccessorAndStreamer(accessor, nil)) + + idx := shwap.SampleCoords{Row: tt.rowIdx, Col: tt.colIdx} + + _, err := validation.Sample(context.Background(), idx) + if tt.expectFail { + require.ErrorIs(t, err, shwap.ErrInvalidID) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestValidation_AxisHalf(t *testing.T) { + tests := []struct { + name string + axisType rsmt2d.Axis + axisIdx int + odsSize int + expectFail bool + }{ + {"ValidIndex", rsmt2d.Row, 2, 4, false}, + {"OutOfBounds", rsmt2d.Col, 8, 4, true}, + {"NegativeIndex", rsmt2d.Row, -1, 4, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + randEDS := edstest.RandEDS(t, tt.odsSize) + accessor := &Rsmt2D{ExtendedDataSquare: randEDS} + validation := WithValidation(AccessorAndStreamer(accessor, nil)) + + _, err := validation.AxisHalf(context.Background(), tt.axisType, tt.axisIdx) + if tt.expectFail { + require.ErrorIs(t, err, shwap.ErrInvalidID) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestValidation_RowNamespaceData(t *testing.T) { + tests := []struct { + name string + rowIdx int + odsSize int + expectFail bool + }{ + {"ValidIndex", 3, 4, false}, + {"OutOfBounds", 8, 4, true}, + {"NegativeIndex", -1, 4, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + randEDS := edstest.RandEDS(t, tt.odsSize) + accessor := &Rsmt2D{ExtendedDataSquare: randEDS} + validation := WithValidation(AccessorAndStreamer(accessor, nil)) + + ns := libshare.RandomNamespace() + _, err := validation.RowNamespaceData(context.Background(), ns, tt.rowIdx) + if tt.expectFail { + require.ErrorIs(t, err, shwap.ErrInvalidID) + } else { + require.True(t, err == nil || errors.Is(err, shwap.ErrNamespaceOutsideRange), err) + } + }) + } +} diff --git a/share/empty.go b/share/empty.go index 9d1a4ff561..6d34184826 100644 --- a/share/empty.go +++ b/share/empty.go @@ -5,35 +5,34 @@ import ( "fmt" "sync" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/go-square/shares" + "github.com/celestiaorg/celestia-app/v3/pkg/da" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" ) -// EmptyRoot returns Root of the empty block EDS. -func EmptyRoot() *Root { +// EmptyEDSDataHash returns DataHash of the empty block EDS. +func EmptyEDSDataHash() DataHash { initEmpty() - return emptyBlockRoot + return emptyBlockDataHash } -// EmptyExtendedDataSquare returns the EDS of the empty block data square. -func EmptyExtendedDataSquare() *rsmt2d.ExtendedDataSquare { +// EmptyEDSRoots returns AxisRoots of the empty block EDS. +func EmptyEDSRoots() *AxisRoots { initEmpty() - return emptyBlockEDS + return emptyBlockRoots } -// EmptyBlockShares returns the shares of the empty block. -func EmptyBlockShares() []Share { +// EmptyEDS returns the EDS of the empty block data square. +func EmptyEDS() *rsmt2d.ExtendedDataSquare { initEmpty() - return emptyBlockShares + return emptyBlockEDS } var ( - emptyOnce sync.Once - emptyBlockRoot *Root - emptyBlockEDS *rsmt2d.ExtendedDataSquare - emptyBlockShares []Share + emptyOnce sync.Once + emptyBlockDataHash DataHash + emptyBlockRoots *AxisRoots + emptyBlockEDS *rsmt2d.ExtendedDataSquare ) // initEmpty enables lazy initialization for constant empty block data. @@ -43,25 +42,25 @@ func initEmpty() { func computeEmpty() { // compute empty block EDS and DAH for it - result := shares.TailPaddingShares(appconsts.MinShareCount) - emptyBlockShares = shares.ToBytes(result) + result := libshare.TailPaddingShares(libshare.MinShareCount) + rawEmptyBlockShares := libshare.ToBytes(result) - eds, err := da.ExtendShares(emptyBlockShares) + eds, err := da.ExtendShares(rawEmptyBlockShares) if err != nil { panic(fmt.Errorf("failed to create empty EDS: %w", err)) } emptyBlockEDS = eds - emptyBlockRoot, err = NewRoot(eds) + emptyBlockRoots, err = NewAxisRoots(eds) if err != nil { panic(fmt.Errorf("failed to create empty DAH: %w", err)) } minDAH := da.MinDataAvailabilityHeader() - if !bytes.Equal(minDAH.Hash(), emptyBlockRoot.Hash()) { + if !bytes.Equal(minDAH.Hash(), emptyBlockRoots.Hash()) { panic(fmt.Sprintf("mismatch in calculated minimum DAH and minimum DAH from celestia-app, "+ - "expected %s, got %s", minDAH.String(), emptyBlockRoot.String())) + "expected %s, got %s", minDAH.String(), emptyBlockRoots.String())) } // precompute Hash, so it's cached internally to avoid potential races - emptyBlockRoot.Hash() + emptyBlockDataHash = emptyBlockRoots.Hash() } diff --git a/share/getter.go b/share/getter.go deleted file mode 100644 index d824134152..0000000000 --- a/share/getter.go +++ /dev/null @@ -1,97 +0,0 @@ -package share - -import ( - "context" - "errors" - "fmt" - - "github.com/celestiaorg/nmt" - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/header" -) - -var ( - // ErrNotFound is used to indicate that requested data could not be found. - ErrNotFound = errors.New("share: data not found") - // ErrOutOfBounds is used to indicate that a passed row or column index is out of bounds of the - // square size. - ErrOutOfBounds = errors.New("share: row or column index is larger than square size") -) - -// Getter interface provides a set of accessors for shares by the Root. -// Automatically verifies integrity of shares(exceptions possible depending on the implementation). -// -//go:generate mockgen -destination=mocks/getter.go -package=mocks . Getter -type Getter interface { - // GetShare gets a Share by coordinates in EDS. - GetShare(ctx context.Context, header *header.ExtendedHeader, row, col int) (Share, error) - - // GetEDS gets the full EDS identified by the given extended header. - GetEDS(context.Context, *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) - - // GetSharesByNamespace gets all shares from an EDS within the given namespace. - // Shares are returned in a row-by-row order if the namespace spans multiple rows. - // Inclusion of returned data could be verified using Verify method on NamespacedShares. - // If no shares are found for target namespace non-inclusion could be also verified by calling - // Verify method. - GetSharesByNamespace(context.Context, *header.ExtendedHeader, Namespace) (NamespacedShares, error) -} - -// NamespacedShares represents all shares with proofs within a specific namespace of an EDS. -type NamespacedShares []NamespacedRow - -// Flatten returns the concatenated slice of all NamespacedRow shares. -func (ns NamespacedShares) Flatten() []Share { - var shares []Share - for _, row := range ns { - shares = append(shares, row.Shares...) - } - return shares -} - -// NamespacedRow represents all shares with proofs within a specific namespace of a single EDS row. -type NamespacedRow struct { - Shares []Share `json:"shares"` - Proof *nmt.Proof `json:"proof"` -} - -// Verify validates NamespacedShares by checking every row with nmt inclusion proof. -func (ns NamespacedShares) Verify(root *Root, namespace Namespace) error { - var originalRoots [][]byte - for _, row := range root.RowRoots { - if !namespace.IsOutsideRange(row, row) { - originalRoots = append(originalRoots, row) - } - } - - if len(originalRoots) != len(ns) { - return fmt.Errorf("amount of rows differs between root and namespace shares: expected %d, got %d", - len(originalRoots), len(ns)) - } - - for i, row := range ns { - // verify row data against row hash from original root - if !row.verify(originalRoots[i], namespace) { - return fmt.Errorf("row verification failed: row %d doesn't match original root: %s", i, root.String()) - } - } - return nil -} - -// verify validates the row using nmt inclusion proof. -func (row *NamespacedRow) verify(rowRoot []byte, namespace Namespace) bool { - // construct nmt leaves from shares by prepending namespace - leaves := make([][]byte, 0, len(row.Shares)) - for _, shr := range row.Shares { - leaves = append(leaves, append(GetNamespace(shr), shr...)) - } - - // verify namespace - return row.Proof.VerifyNamespace( - NewSHA256Hasher(), - namespace.ToNMT(), - leaves, - rowRoot, - ) -} diff --git a/share/getters/getter_test.go b/share/getters/getter_test.go deleted file mode 100644 index 47a3c4ac24..0000000000 --- a/share/getters/getter_test.go +++ /dev/null @@ -1,355 +0,0 @@ -package getters - -import ( - "context" - "os" - "sync" - "testing" - "time" - - "github.com/ipfs/boxo/exchange/offline" - "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" - dsbadger "github.com/ipfs/go-ds-badger4" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/header/headertest" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/sharetest" -) - -func TestStoreGetter(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - tmpDir := t.TempDir() - storeCfg := eds.DefaultParameters() - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - edsStore, err := eds.NewStore(storeCfg, tmpDir, ds) - require.NoError(t, err) - - err = edsStore.Start(ctx) - require.NoError(t, err) - - sg := NewStoreGetter(edsStore) - - t.Run("GetShare", func(t *testing.T) { - randEds, eh := randomEDS(t) - err = edsStore.Put(ctx, eh.DAH.Hash(), randEds) - require.NoError(t, err) - - squareSize := int(randEds.Width()) - for i := 0; i < squareSize; i++ { - for j := 0; j < squareSize; j++ { - share, err := sg.GetShare(ctx, eh, i, j) - require.NoError(t, err) - assert.Equal(t, randEds.GetCell(uint(i), uint(j)), share) - } - } - - // doesn't panic on indexes too high - _, err := sg.GetShare(ctx, eh, squareSize, squareSize) - require.ErrorIs(t, err, share.ErrOutOfBounds) - - // root not found - _, eh = randomEDS(t) - _, err = sg.GetShare(ctx, eh, 0, 0) - require.ErrorIs(t, err, share.ErrNotFound) - }) - - t.Run("GetEDS", func(t *testing.T) { - randEds, eh := randomEDS(t) - err = edsStore.Put(ctx, eh.DAH.Hash(), randEds) - require.NoError(t, err) - - retrievedEDS, err := sg.GetEDS(ctx, eh) - require.NoError(t, err) - assert.True(t, randEds.Equals(retrievedEDS)) - - // root not found - emptyRoot := da.MinDataAvailabilityHeader() - eh.DAH = &emptyRoot - _, err = sg.GetEDS(ctx, eh) - require.ErrorIs(t, err, share.ErrNotFound) - }) - - t.Run("GetSharesByNamespace", func(t *testing.T) { - randEds, namespace, eh := randomEDSWithDoubledNamespace(t, 4) - err = edsStore.Put(ctx, eh.DAH.Hash(), randEds) - require.NoError(t, err) - - shares, err := sg.GetSharesByNamespace(ctx, eh, namespace) - require.NoError(t, err) - require.NoError(t, shares.Verify(eh.DAH, namespace)) - assert.Len(t, shares.Flatten(), 2) - - // namespace not found - randNamespace := sharetest.RandV0Namespace() - emptyShares, err := sg.GetSharesByNamespace(ctx, eh, randNamespace) - require.NoError(t, err) - require.Nil(t, emptyShares.Flatten()) - - // root not found - emptyRoot := da.MinDataAvailabilityHeader() - eh.DAH = &emptyRoot - _, err = sg.GetSharesByNamespace(ctx, eh, namespace) - require.ErrorIs(t, err, share.ErrNotFound) - }) - - t.Run("GetSharesFromNamespace removes corrupted shard", func(t *testing.T) { - randEds, namespace, eh := randomEDSWithDoubledNamespace(t, 4) - err = edsStore.Put(ctx, eh.DAH.Hash(), randEds) - require.NoError(t, err) - - // available - shares, err := sg.GetSharesByNamespace(ctx, eh, namespace) - require.NoError(t, err) - require.NoError(t, shares.Verify(eh.DAH, namespace)) - assert.Len(t, shares.Flatten(), 2) - - // 'corrupt' existing CAR by overwriting with a random EDS - f, err := os.OpenFile(tmpDir+"/blocks/"+eh.DAH.String(), os.O_WRONLY, 0o644) - require.NoError(t, err) - edsToOverwriteWith, eh := randomEDS(t) - err = eds.WriteEDS(ctx, edsToOverwriteWith, f) - require.NoError(t, err) - - shares, err = sg.GetSharesByNamespace(ctx, eh, namespace) - require.ErrorIs(t, err, share.ErrNotFound) - require.Nil(t, shares) - - // corruption detected, shard is removed - // try every 200ms until it passes or the context ends - ticker := time.NewTicker(200 * time.Millisecond) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - t.Fatal("context ended before successful retrieval") - case <-ticker.C: - has, err := edsStore.Has(ctx, eh.DAH.Hash()) - if err != nil { - t.Fatal(err) - } - if !has { - require.NoError(t, err) - return - } - } - } - }) -} - -func TestIPLDGetter(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - storeCfg := eds.DefaultParameters() - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - edsStore, err := eds.NewStore(storeCfg, t.TempDir(), ds) - require.NoError(t, err) - - err = edsStore.Start(ctx) - require.NoError(t, err) - - bStore := edsStore.Blockstore() - bserv := ipld.NewBlockservice(bStore, offline.Exchange(edsStore.Blockstore())) - sg := NewIPLDGetter(bserv) - - t.Run("GetShare", func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) - t.Cleanup(cancel) - - randEds, eh := randomEDS(t) - err = edsStore.Put(ctx, eh.DAH.Hash(), randEds) - require.NoError(t, err) - - squareSize := int(randEds.Width()) - for i := 0; i < squareSize; i++ { - for j := 0; j < squareSize; j++ { - share, err := sg.GetShare(ctx, eh, i, j) - require.NoError(t, err) - assert.Equal(t, randEds.GetCell(uint(i), uint(j)), share) - } - } - - // doesn't panic on indexes too high - _, err := sg.GetShare(ctx, eh, squareSize+1, squareSize+1) - require.ErrorIs(t, err, share.ErrOutOfBounds) - - // root not found - _, eh = randomEDS(t) - _, err = sg.GetShare(ctx, eh, 0, 0) - require.ErrorIs(t, err, share.ErrNotFound) - }) - - t.Run("GetEDS", func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) - t.Cleanup(cancel) - - randEds, eh := randomEDS(t) - err = edsStore.Put(ctx, eh.DAH.Hash(), randEds) - require.NoError(t, err) - - retrievedEDS, err := sg.GetEDS(ctx, eh) - require.NoError(t, err) - assert.True(t, randEds.Equals(retrievedEDS)) - - // Ensure blocks still exist after cleanup - colRoots, _ := retrievedEDS.ColRoots() - has, err := bStore.Has(ctx, ipld.MustCidFromNamespacedSha256(colRoots[0])) - assert.NoError(t, err) - assert.True(t, has) - }) - - t.Run("GetSharesByNamespace", func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) - t.Cleanup(cancel) - - randEds, namespace, eh := randomEDSWithDoubledNamespace(t, 4) - err = edsStore.Put(ctx, eh.DAH.Hash(), randEds) - require.NoError(t, err) - - // first check that shares are returned correctly if they exist - shares, err := sg.GetSharesByNamespace(ctx, eh, namespace) - require.NoError(t, err) - require.NoError(t, shares.Verify(eh.DAH, namespace)) - assert.Len(t, shares.Flatten(), 2) - - // namespace not found - randNamespace := sharetest.RandV0Namespace() - emptyShares, err := sg.GetSharesByNamespace(ctx, eh, randNamespace) - require.NoError(t, err) - require.Nil(t, emptyShares.Flatten()) - - // nid doesn't exist in root - emptyRoot := da.MinDataAvailabilityHeader() - eh.DAH = &emptyRoot - emptyShares, err = sg.GetSharesByNamespace(ctx, eh, namespace) - require.NoError(t, err) - require.Empty(t, emptyShares.Flatten()) - }) -} - -// BenchmarkIPLDGetterOverBusyCache benchmarks the performance of the IPLDGetter when the -// cache size of the underlying blockstore is less than the number of blocks being requested in -// parallel. This is to ensure performance doesn't degrade when the cache is being frequently -// evicted. -// BenchmarkIPLDGetterOverBusyCache-10/128 1 12460428417 ns/op (~12s) -func BenchmarkIPLDGetterOverBusyCache(b *testing.B) { - const ( - blocks = 10 - size = 128 - ) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - b.Cleanup(cancel) - - dir := b.TempDir() - ds, err := dsbadger.NewDatastore(dir, &dsbadger.DefaultOptions) - require.NoError(b, err) - - newStore := func(params *eds.Parameters) *eds.Store { - edsStore, err := eds.NewStore(params, dir, ds) - require.NoError(b, err) - err = edsStore.Start(ctx) - require.NoError(b, err) - return edsStore - } - edsStore := newStore(eds.DefaultParameters()) - - // generate EDSs and store them - headers := make([]*header.ExtendedHeader, blocks) - for i := range headers { - eds := edstest.RandEDS(b, size) - dah, err := da.NewDataAvailabilityHeader(eds) - require.NoError(b, err) - err = edsStore.Put(ctx, dah.Hash(), eds) - require.NoError(b, err) - - eh := headertest.RandExtendedHeader(b) - eh.DAH = &dah - - // store cids for read loop later - headers[i] = eh - } - - // restart store to clear cache - require.NoError(b, edsStore.Stop(ctx)) - - // set BlockstoreCacheSize to 1 to force eviction on every read - params := eds.DefaultParameters() - params.BlockstoreCacheSize = 1 - edsStore = newStore(params) - bstore := edsStore.Blockstore() - bserv := ipld.NewBlockservice(bstore, offline.Exchange(bstore)) - - // start client - getter := NewIPLDGetter(bserv) - - // request blocks in parallel - b.ResetTimer() - g := sync.WaitGroup{} - g.Add(blocks) - for _, h := range headers { - go func() { - defer g.Done() - _, err := getter.GetEDS(ctx, h) - require.NoError(b, err) - }() - } - g.Wait() -} - -func randomEDS(t *testing.T) (*rsmt2d.ExtendedDataSquare, *header.ExtendedHeader) { - eds := edstest.RandEDS(t, 4) - dah, err := share.NewRoot(eds) - require.NoError(t, err) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - return eds, eh -} - -// randomEDSWithDoubledNamespace generates a random EDS and ensures that there are two shares in the -// middle that share a namespace. -// -//nolint:dupword -func randomEDSWithDoubledNamespace( - t *testing.T, - size int, -) (*rsmt2d.ExtendedDataSquare, []byte, *header.ExtendedHeader) { - n := size * size - randShares := sharetest.RandShares(t, n) - idx1 := (n - 1) / 2 - idx2 := n / 2 - - // Make it so that the two shares in two different rows have a common - // namespace. For example if size=4, the original data square looks like - // this: - // _ _ _ _ - // _ _ _ D - // D _ _ _ - // _ _ _ _ - // where the D shares have a common namespace. - copy(share.GetNamespace(randShares[idx2]), share.GetNamespace(randShares[idx1])) - - eds, err := rsmt2d.ComputeExtendedDataSquare( - randShares, - share.DefaultRSMT2DCodec(), - wrapper.NewConstructor(uint64(size)), - ) - require.NoError(t, err, "failure to recompute the extended data square") - dah, err := share.NewRoot(eds) - require.NoError(t, err) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - - return eds, share.GetNamespace(randShares[idx1]), eh -} diff --git a/share/getters/ipld.go b/share/getters/ipld.go deleted file mode 100644 index e9c930248d..0000000000 --- a/share/getters/ipld.go +++ /dev/null @@ -1,165 +0,0 @@ -package getters - -import ( - "context" - "errors" - "fmt" - "sync" - "sync/atomic" - - "github.com/ipfs/boxo/blockservice" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/eds/byzantine" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -var _ share.Getter = (*IPLDGetter)(nil) - -// IPLDGetter is a share.Getter that retrieves shares from the bitswap network. Result caching is -// handled by the provided blockservice. A blockservice session will be created for retrieval if the -// passed context is wrapped with WithSession. -type IPLDGetter struct { - rtrv *eds.Retriever - bServ blockservice.BlockService -} - -// NewIPLDGetter creates a new share.Getter that retrieves shares from the bitswap network. -func NewIPLDGetter(bServ blockservice.BlockService) *IPLDGetter { - return &IPLDGetter{ - rtrv: eds.NewRetriever(bServ), - bServ: bServ, - } -} - -// GetShare gets a single share at the given EDS coordinates from the bitswap network. -func (ig *IPLDGetter) GetShare(ctx context.Context, header *header.ExtendedHeader, row, col int) (share.Share, error) { - var err error - ctx, span := tracer.Start(ctx, "ipld/get-share", trace.WithAttributes( - attribute.Int("row", row), - attribute.Int("col", col), - )) - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - dah := header.DAH - upperBound := len(dah.RowRoots) - if row >= upperBound || col >= upperBound { - err := share.ErrOutOfBounds - span.RecordError(err) - return nil, err - } - root, leaf := ipld.Translate(dah, row, col) - - // wrap the blockservice in a session if it has been signaled in the context. - blockGetter := getGetter(ctx, ig.bServ) - s, err := ipld.GetShare(ctx, blockGetter, root, leaf, len(dah.RowRoots)) - if errors.Is(err, ipld.ErrNodeNotFound) { - // convert error to satisfy getter interface contract - err = share.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("getter/ipld: failed to retrieve share: %w", err) - } - - return s, nil -} - -func (ig *IPLDGetter) GetEDS( - ctx context.Context, - header *header.ExtendedHeader, -) (eds *rsmt2d.ExtendedDataSquare, err error) { - ctx, span := tracer.Start(ctx, "ipld/get-eds") - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - // rtrv.Retrieve calls shares.GetShares until enough shares are retrieved to reconstruct the EDS - eds, err = ig.rtrv.Retrieve(ctx, header.DAH) - if errors.Is(err, ipld.ErrNodeNotFound) { - // convert error to satisfy getter interface contract - err = share.ErrNotFound - } - var errByz *byzantine.ErrByzantine - if errors.As(err, &errByz) { - return nil, err - } - if err != nil { - return nil, fmt.Errorf("getter/ipld: failed to retrieve eds: %w", err) - } - return eds, nil -} - -func (ig *IPLDGetter) GetSharesByNamespace( - ctx context.Context, - header *header.ExtendedHeader, - namespace share.Namespace, -) (shares share.NamespacedShares, err error) { - ctx, span := tracer.Start(ctx, "ipld/get-shares-by-namespace", trace.WithAttributes( - attribute.String("namespace", namespace.String()), - )) - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - if err = namespace.ValidateForData(); err != nil { - return nil, err - } - - // wrap the blockservice in a session if it has been signaled in the context. - blockGetter := getGetter(ctx, ig.bServ) - shares, err = eds.CollectSharesByNamespace(ctx, blockGetter, header.DAH, namespace) - if errors.Is(err, ipld.ErrNodeNotFound) { - // convert error to satisfy getter interface contract - err = share.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("getter/ipld: failed to retrieve shares by namespace: %w", err) - } - return shares, nil -} - -var sessionKey = &session{} - -// session is a struct that can optionally be passed by context to the share.Getter methods using -// WithSession to indicate that a blockservice session should be created. -type session struct { - sync.Mutex - atomic.Pointer[blockservice.Session] - ctx context.Context -} - -// WithSession stores an empty session in the context, indicating that a blockservice session should -// be created. -func WithSession(ctx context.Context) context.Context { - return context.WithValue(ctx, sessionKey, &session{ctx: ctx}) -} - -func getGetter(ctx context.Context, service blockservice.BlockService) blockservice.BlockGetter { - s, ok := ctx.Value(sessionKey).(*session) - if !ok { - return service - } - - val := s.Load() - if val != nil { - return val - } - - s.Lock() - defer s.Unlock() - val = s.Load() - if val == nil { - val = blockservice.NewSession(s.ctx, service) - s.Store(val) - } - return val -} diff --git a/share/getters/store.go b/share/getters/store.go deleted file mode 100644 index d66a057c56..0000000000 --- a/share/getters/store.go +++ /dev/null @@ -1,122 +0,0 @@ -package getters - -import ( - "context" - "errors" - "fmt" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/ipld" -) - -var _ share.Getter = (*StoreGetter)(nil) - -// StoreGetter is a share.Getter that retrieves shares from an eds.Store. No results are saved to -// the eds.Store after retrieval. -type StoreGetter struct { - store *eds.Store -} - -// NewStoreGetter creates a new share.Getter that retrieves shares from an eds.Store. -func NewStoreGetter(store *eds.Store) *StoreGetter { - return &StoreGetter{ - store: store, - } -} - -// GetShare gets a single share at the given EDS coordinates from the eds.Store through the -// corresponding CAR-level blockstore. -func (sg *StoreGetter) GetShare(ctx context.Context, header *header.ExtendedHeader, row, col int) (share.Share, error) { - dah := header.DAH - var err error - ctx, span := tracer.Start(ctx, "store/get-share", trace.WithAttributes( - attribute.Int("row", row), - attribute.Int("col", col), - )) - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - upperBound := len(dah.RowRoots) - if row >= upperBound || col >= upperBound { - err := share.ErrOutOfBounds - span.RecordError(err) - return nil, err - } - root, leaf := ipld.Translate(dah, row, col) - bs, err := sg.store.CARBlockstore(ctx, dah.Hash()) - if errors.Is(err, eds.ErrNotFound) { - // convert error to satisfy getter interface contract - err = share.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("getter/store: failed to retrieve blockstore: %w", err) - } - defer func() { - if err := bs.Close(); err != nil { - log.Warnw("closing blockstore", "err", err) - } - }() - - // wrap the read-only CAR blockstore in a getter - blockGetter := eds.NewBlockGetter(bs) - s, err := ipld.GetShare(ctx, blockGetter, root, leaf, len(dah.RowRoots)) - if errors.Is(err, ipld.ErrNodeNotFound) { - // convert error to satisfy getter interface contract - err = share.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("getter/store: failed to retrieve share: %w", err) - } - - return s, nil -} - -// GetEDS gets the EDS identified by the given root from the EDS store. -func (sg *StoreGetter) GetEDS( - ctx context.Context, header *header.ExtendedHeader, -) (data *rsmt2d.ExtendedDataSquare, err error) { - ctx, span := tracer.Start(ctx, "store/get-eds") - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - data, err = sg.store.Get(ctx, header.DAH.Hash()) - if errors.Is(err, eds.ErrNotFound) { - // convert error to satisfy getter interface contract - err = share.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("getter/store: failed to retrieve eds: %w", err) - } - return data, nil -} - -// GetSharesByNamespace gets all EDS shares in the given namespace from the EDS store through the -// corresponding CAR-level blockstore. -func (sg *StoreGetter) GetSharesByNamespace( - ctx context.Context, - header *header.ExtendedHeader, - namespace share.Namespace, -) (shares share.NamespacedShares, err error) { - ctx, span := tracer.Start(ctx, "store/get-shares-by-namespace", trace.WithAttributes( - attribute.String("namespace", namespace.String()), - )) - defer func() { - utils.SetStatusAndEnd(span, err) - }() - - ns, err := eds.RetrieveNamespaceFromStore(ctx, sg.store, header.DAH, namespace) - if err != nil { - return nil, fmt.Errorf("getter/store: %w", err) - } - return ns, nil -} diff --git a/share/getters/testing.go b/share/getters/testing.go deleted file mode 100644 index 665690ffe3..0000000000 --- a/share/getters/testing.go +++ /dev/null @@ -1,76 +0,0 @@ -package getters - -import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-app/v2/pkg/da" - "github.com/celestiaorg/rsmt2d" - - "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/header/headertest" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds/edstest" -) - -// TestGetter provides a testing SingleEDSGetter and the root of the EDS it holds. -func TestGetter(t *testing.T) (share.Getter, *header.ExtendedHeader) { - eds := edstest.RandEDS(t, 8) - dah, err := share.NewRoot(eds) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - require.NoError(t, err) - return &SingleEDSGetter{ - EDS: eds, - }, eh -} - -// SingleEDSGetter contains a single EDS where data is retrieved from. -// Its primary use is testing, and GetSharesByNamespace is not supported. -type SingleEDSGetter struct { - EDS *rsmt2d.ExtendedDataSquare -} - -// GetShare gets a share from a kept EDS if exist and if the correct root is given. -func (seg *SingleEDSGetter) GetShare( - _ context.Context, - header *header.ExtendedHeader, - row, col int, -) (share.Share, error) { - err := seg.checkRoot(header.DAH) - if err != nil { - return nil, err - } - return seg.EDS.GetCell(uint(row), uint(col)), nil -} - -// GetEDS returns a kept EDS if the correct root is given. -func (seg *SingleEDSGetter) GetEDS( - _ context.Context, - header *header.ExtendedHeader, -) (*rsmt2d.ExtendedDataSquare, error) { - err := seg.checkRoot(header.DAH) - if err != nil { - return nil, err - } - return seg.EDS, nil -} - -// GetSharesByNamespace returns NamespacedShares from a kept EDS if the correct root is given. -func (seg *SingleEDSGetter) GetSharesByNamespace(context.Context, *header.ExtendedHeader, share.Namespace, -) (share.NamespacedShares, error) { - panic("SingleEDSGetter: GetSharesByNamespace is not implemented") -} - -func (seg *SingleEDSGetter) checkRoot(root *share.Root) error { - dah, err := da.NewDataAvailabilityHeader(seg.EDS) - if err != nil { - return err - } - if !root.Equals(&dah) { - return fmt.Errorf("unknown EDS: have %s, asked %s", dah.String(), root.String()) - } - return nil -} diff --git a/share/ipld/add.go b/share/ipld/add.go index 42705557ae..0e0b6346dc 100644 --- a/share/ipld/add.go +++ b/share/ipld/add.go @@ -6,7 +6,8 @@ import ( "github.com/ipfs/boxo/blockservice" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" @@ -18,7 +19,7 @@ import ( // ipld.NodeAdder. func AddShares( ctx context.Context, - shares []share.Share, + shares []libshare.Share, adder blockservice.BlockService, ) (*rsmt2d.ExtendedDataSquare, error) { if len(shares) == 0 { @@ -30,7 +31,7 @@ func AddShares( // create the nmt wrapper to generate row and col commitments // recompute the eds eds, err := rsmt2d.ComputeExtendedDataSquare( - shares, + libshare.ToBytes(shares), share.DefaultRSMT2DCodec(), wrapper.NewConstructor(uint64(squareSize), nmt.NodeVisitor(batchAdder.Visit)), @@ -47,7 +48,7 @@ func AddShares( return eds, batchAdder.Commit() } -// ImportShares imports flattened pieces of data into Extended Data square and saves it in +// ImportShares imports flattened pieces of data into Extended Data Square and saves it in // blockservice.BlockService func ImportShares( ctx context.Context, diff --git a/share/ipld/blockserv.go b/share/ipld/blockserv.go index 2ed2a21c77..b7a9bf84e9 100644 --- a/share/ipld/blockserv.go +++ b/share/ipld/blockserv.go @@ -9,7 +9,7 @@ import ( ) // NewBlockservice constructs Blockservice for fetching NMTrees. -func NewBlockservice(bs blockstore.Blockstore, exchange exchange.Interface) blockservice.BlockService { +func NewBlockservice(bs blockstore.Blockstore, exchange exchange.SessionExchange) blockservice.BlockService { return blockservice.New(bs, exchange, blockservice.WithAllowlist(defaultAllowlist)) } diff --git a/share/ipld/corrupted_data_test.go b/share/ipld/corrupted_data_test.go deleted file mode 100644 index 0d0af6dd35..0000000000 --- a/share/ipld/corrupted_data_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package ipld_test - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-node/header/headertest" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/availability/full" - availability_test "github.com/celestiaorg/celestia-node/share/availability/test" - "github.com/celestiaorg/celestia-node/share/getters" -) - -// sharesAvailableTimeout is an arbitrarily picked interval of time in which a TestNode is expected -// to be able to complete a SharesAvailable request from a connected peer in a TestDagNet. -const sharesAvailableTimeout = 2 * time.Second - -// TestNamespaceHasher_CorruptedData is an integration test that verifies that the NamespaceHasher -// of a recipient of corrupted data will not panic, and will throw away the corrupted data. -func TestNamespaceHasher_CorruptedData(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - net := availability_test.NewTestDAGNet(ctx, t) - - requester := full.Node(net) - provider, mockBS := availability_test.MockNode(t, net) - provider.Availability = full.TestAvailability(t, getters.NewIPLDGetter(provider.BlockService)) - net.ConnectAll() - - // before the provider starts attacking, we should be able to retrieve successfully. We pass a size - // 16 block, but this is not important to the test and any valid block size behaves the same. - root := availability_test.RandFillBS(t, 16, provider.BlockService) - - eh := headertest.RandExtendedHeaderWithRoot(t, root) - getCtx, cancelGet := context.WithTimeout(ctx, sharesAvailableTimeout) - t.Cleanup(cancelGet) - err := requester.SharesAvailable(getCtx, eh) - require.NoError(t, err) - - // clear the storage of the requester so that it must retrieve again, then start attacking - // we reinitialize the node to clear the eds store - requester = full.Node(net) - mockBS.Attacking = true - getCtx, cancelGet = context.WithTimeout(ctx, sharesAvailableTimeout) - t.Cleanup(cancelGet) - err = requester.SharesAvailable(getCtx, eh) - require.ErrorIs(t, err, share.ErrNotAvailable) -} diff --git a/share/ipld/delete.go b/share/ipld/delete.go deleted file mode 100644 index 6166f8eadb..0000000000 --- a/share/ipld/delete.go +++ /dev/null @@ -1,29 +0,0 @@ -package ipld - -import ( - "context" - "errors" - - "github.com/ipfs/boxo/blockservice" - "github.com/ipfs/go-cid" -) - -// DeleteNode deletes the Node behind the CID. It also recursively deletes all other Nodes linked -// behind the Node. -func DeleteNode(ctx context.Context, bserv blockservice.BlockService, cid cid.Cid) error { - blk, err := GetNode(ctx, bserv, cid) - if err != nil { - if errors.Is(err, ErrNodeNotFound) { - return nil - } - return err - } - - for _, lnk := range blk.Links() { - if err := DeleteNode(ctx, bserv, lnk.Cid); err != nil { - return err - } - } - - return bserv.DeleteBlock(ctx, cid) -} diff --git a/share/ipld/delete_test.go b/share/ipld/delete_test.go deleted file mode 100644 index 00e6958285..0000000000 --- a/share/ipld/delete_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package ipld - -import ( - "context" - "testing" - "time" - - "github.com/ipfs/boxo/blockstore" - "github.com/ipfs/boxo/exchange/offline" - "github.com/ipfs/go-datastore" - "github.com/ipfs/go-datastore/sync" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/celestia-node/share/sharetest" -) - -func TestDeleteNode_FullSquare(t *testing.T) { - const size = 8 - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - bServ := NewMemBlockservice() - - shares := sharetest.RandShares(t, size*size) - eds, err := AddShares(ctx, shares, bServ) - require.NoError(t, err) - - keys, err := bServ.Blockstore().AllKeysChan(ctx) - require.NoError(t, err) - - var preDeleteCount int - for range keys { - preDeleteCount++ - } - require.NotZero(t, preDeleteCount) - - rowRoots, err := eds.RowRoots() - require.NoError(t, err) - for _, root := range rowRoots { - err := DeleteNode(ctx, bServ, MustCidFromNamespacedSha256(root)) - require.NoError(t, err) - } - colRoots, err := eds.ColRoots() - require.NoError(t, err) - for _, root := range colRoots { - err := DeleteNode(ctx, bServ, MustCidFromNamespacedSha256(root)) - require.NoError(t, err) - } - - keys, err = bServ.Blockstore().AllKeysChan(ctx) - require.NoError(t, err) - - var postDeleteCount int - for range keys { - postDeleteCount++ - } - require.Zero(t, postDeleteCount) -} - -func TestDeleteNode_Sample(t *testing.T) { - const size = 8 - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - full := NewMemBlockservice() - - shares := sharetest.RandShares(t, size*size) - eds, err := AddShares(ctx, shares, full) - require.NoError(t, err) - - rowRoots, err := eds.RowRoots() - require.NoError(t, err) - - bstore := blockstore.NewBlockstore(sync.MutexWrap(datastore.NewMapDatastore())) - light := NewBlockservice(bstore, offline.Exchange(full.Blockstore())) - - cid := MustCidFromNamespacedSha256(rowRoots[0]) - _, err = GetShare(ctx, light, cid, 0, len(rowRoots)) - require.NoError(t, err) - - keys, err := light.Blockstore().AllKeysChan(ctx) - require.NoError(t, err) - - var preDeleteCount int - for range keys { - preDeleteCount++ - } - require.NotZero(t, preDeleteCount) - - for _, root := range rowRoots { - err := DeleteNode(ctx, light, MustCidFromNamespacedSha256(root)) - require.NoError(t, err) - } - - keys, err = light.Blockstore().AllKeysChan(ctx) - require.NoError(t, err) - - var postDeleteCount int - for range keys { - postDeleteCount++ - } - require.Zero(t, postDeleteCount) -} diff --git a/share/ipld/get.go b/share/ipld/get.go index adf2ffa8c5..9e85f7ccc9 100644 --- a/share/ipld/get.go +++ b/share/ipld/get.go @@ -157,59 +157,19 @@ func GetLeaves(ctx context.Context, wg.Wait() } -// GetProof fetches and returns the leaf's Merkle Proof. -// It walks down the IPLD NMT tree until it reaches the leaf and returns collected proof -func GetProof( - ctx context.Context, - bGetter blockservice.BlockGetter, - root cid.Cid, - proof []cid.Cid, - leaf, total int, -) ([]cid.Cid, error) { - // request the node - nd, err := GetNode(ctx, bGetter, root) - if err != nil { - return nil, err - } - // look for links - lnks := nd.Links() - if len(lnks) == 0 { - p := make([]cid.Cid, len(proof)) - copy(p, proof) - return p, nil - } - - // route walk to appropriate children - total /= 2 // as we are using binary tree, every step decreases total leaves in a half - if leaf < total { - root = lnks[0].Cid // if target leave on the left, go with walk down the first children - proof = append(proof, lnks[1].Cid) - } else { - root, leaf = lnks[1].Cid, leaf-total // otherwise go down the second - proof, err = GetProof(ctx, bGetter, root, proof, leaf, total) - if err != nil { - return nil, err - } - return append(proof, lnks[0].Cid), nil - } - - // recursively walk down through selected children - return GetProof(ctx, bGetter, root, proof, leaf, total) -} - // chanGroup implements an atomic wait group, closing a jobs chan // when fully done. type chanGroup struct { jobs chan job - counter int64 + counter atomic.Int64 } func (w *chanGroup) add(count int64) { - atomic.AddInt64(&w.counter, count) + w.counter.Add(count) } func (w *chanGroup) done() { - numRemaining := atomic.AddInt64(&w.counter, -1) + numRemaining := w.counter.Add(-1) // Close channel if this job was the last one if numRemaining == 0 { diff --git a/share/ipld/get_shares.go b/share/ipld/get_shares.go index 1640720b48..e76a54ee7c 100644 --- a/share/ipld/get_shares.go +++ b/share/ipld/get_shares.go @@ -7,9 +7,8 @@ import ( "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" - - "github.com/celestiaorg/celestia-node/share" ) // GetShare fetches and returns the data for leaf `leafIndex` of root `rootCid`. @@ -19,21 +18,25 @@ func GetShare( rootCid cid.Cid, leafIndex int, totalLeafs int, // this corresponds to the extended square width -) (share.Share, error) { +) (libshare.Share, error) { nd, err := GetLeaf(ctx, bGetter, rootCid, leafIndex, totalLeafs) if err != nil { - return nil, err + return libshare.Share{}, err } - return leafToShare(nd), nil + sh, err := libshare.NewShare(nd.RawData()[libshare.NamespaceSize:]) + if err != nil { + return libshare.Share{}, err + } + return *sh, nil } // GetShares walks the tree of a given root and puts shares into the given 'put' func. // Does not return any error, and returns/unblocks only on success // (got all shares) or on context cancellation. -func GetShares(ctx context.Context, bg blockservice.BlockGetter, root cid.Cid, shares int, put func(int, share.Share)) { +func GetShares(ctx context.Context, bg blockservice.BlockGetter, root cid.Cid, shares int, put func(int, []byte)) { putNode := func(i int, leaf format.Node) { - put(i, leafToShare(leaf)) + put(i, leaf.RawData()[libshare.NamespaceSize:]) } GetLeaves(ctx, bg, root, shares, putNode) } @@ -44,12 +47,13 @@ func GetShares(ctx context.Context, bg blockservice.BlockGetter, root cid.Cid, s func GetSharesByNamespace( ctx context.Context, bGetter blockservice.BlockGetter, - root cid.Cid, - namespace share.Namespace, + root []byte, + namespace libshare.Namespace, maxShares int, -) ([]share.Share, *nmt.Proof, error) { +) ([]libshare.Share, *nmt.Proof, error) { + rootCid := MustCidFromNamespacedSha256(root) data := NewNamespaceData(maxShares, namespace, WithLeaves(), WithProofs()) - err := data.CollectLeavesByNamespace(ctx, bGetter, root) + err := data.CollectLeavesByNamespace(ctx, bGetter, rootCid) if err != nil { return nil, nil, err } @@ -59,18 +63,15 @@ func GetSharesByNamespace( return nil, data.Proof(), nil } - shares := make([]share.Share, len(leaves)) + shares := make([]libshare.Share, len(leaves)) for i, leaf := range leaves { if leaf != nil { - shares[i] = leafToShare(leaf) + sh, err := libshare.NewShare(leaf.RawData()[libshare.NamespaceSize:]) + if err != nil { + return nil, nil, err + } + shares[i] = *sh } } return shares, data.Proof(), nil } - -// leafToShare converts an NMT leaf into a Share. -func leafToShare(nd format.Node) share.Share { - // * Additional namespace is prepended so that parity data can be identified with a parity - // namespace, which we cut off - return share.GetData(nd.RawData()) -} diff --git a/share/ipld/get_shares_test.go b/share/ipld/get_shares_test.go index 0e150dc811..5732e5565e 100644 --- a/share/ipld/get_shares_test.go +++ b/share/ipld/get_shares_test.go @@ -1,7 +1,6 @@ package ipld import ( - "bytes" "context" "errors" mrand "math/rand" @@ -15,13 +14,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/wrapper" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/libs/utils" "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/sharetest" ) func TestGetShare(t *testing.T) { @@ -32,7 +31,8 @@ func TestGetShare(t *testing.T) { bServ := NewMemBlockservice() // generate random shares for the nmt - shares := sharetest.RandShares(t, size*size) + shares, err := libshare.RandShares(size * size) + require.NoError(t, err) eds, err := AddShares(ctx, shares, bServ) require.NoError(t, err) @@ -54,12 +54,14 @@ func TestBlockRecovery(t *testing.T) { extendedShareCount := extendedSquareWidth * extendedSquareWidth // generate test data - quarterShares := sharetest.RandShares(t, shareCount) - allShares := sharetest.RandShares(t, shareCount) + quarterShares, err := libshare.RandShares(shareCount) + require.NoError(t, err) + allShares, err := libshare.RandShares(shareCount) + require.NoError(t, err) testCases := []struct { name string - shares []share.Share + shares []libshare.Share expectErr bool errString string d int // number of shares to delete @@ -74,7 +76,7 @@ func TestBlockRecovery(t *testing.T) { squareSize := utils.SquareSize(len(tc.shares)) testEds, err := rsmt2d.ComputeExtendedDataSquare( - tc.shares, + libshare.ToBytes(tc.shares), share.DefaultRSMT2DCodec(), wrapper.NewConstructor(squareSize), ) @@ -115,18 +117,19 @@ func TestBlockRecovery(t *testing.T) { func Test_ConvertEDStoShares(t *testing.T) { squareWidth := 16 - shares := sharetest.RandShares(t, squareWidth*squareWidth) + shares, err := libshare.RandShares(squareWidth * squareWidth) + require.NoError(t, err) // compute extended square testEds, err := rsmt2d.ComputeExtendedDataSquare( - shares, + libshare.ToBytes(shares), share.DefaultRSMT2DCodec(), wrapper.NewConstructor(uint64(squareWidth)), ) require.NoError(t, err) resshares := testEds.FlattenedODS() - require.Equal(t, shares, resshares) + require.Equal(t, libshare.ToBytes(shares), resshares) } // removes d shares from data @@ -148,19 +151,24 @@ func TestGetSharesByNamespace(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) t.Cleanup(cancel) bServ := NewMemBlockservice() + sh0, err := libshare.RandShares(4) + require.NoError(t, err) + + sh1, err := libshare.RandShares(4) + require.NoError(t, err) tests := []struct { - rawData []share.Share + rawData []libshare.Share }{ - {rawData: sharetest.RandShares(t, 4)}, - {rawData: sharetest.RandShares(t, 16)}, + {rawData: sh0}, + {rawData: sh1}, } for i, tt := range tests { t.Run(strconv.Itoa(i), func(t *testing.T) { // choose random namespace from rand shares expected := tt.rawData[len(tt.rawData)/2] - namespace := share.GetNamespace(expected) + namespace := expected.Namespace() // change rawData to contain several shares with same namespace tt.rawData[(len(tt.rawData)/2)+1] = expected @@ -168,12 +176,11 @@ func TestGetSharesByNamespace(t *testing.T) { eds, err := AddShares(ctx, tt.rawData, bServ) require.NoError(t, err) - var shares []share.Share + var shares []libshare.Share rowRoots, err := eds.RowRoots() require.NoError(t, err) for _, row := range rowRoots { - rcid := MustCidFromNamespacedSha256(row) - rowShares, _, err := GetSharesByNamespace(ctx, bServ, rcid, namespace, len(rowRoots)) + rowShares, _, err := GetSharesByNamespace(ctx, bServ, row, namespace, len(rowRoots)) if errors.Is(err, ErrNamespaceOutsideRange) { continue } @@ -195,12 +202,13 @@ func TestCollectLeavesByNamespace_IncompleteData(t *testing.T) { t.Cleanup(cancel) bServ := NewMemBlockservice() - shares := sharetest.RandShares(t, 16) + shares, err := libshare.RandShares(16) + require.NoError(t, err) // set all shares to the same namespace id - namespace := share.GetNamespace(shares[0]) + namespace := shares[0].Namespace() for _, shr := range shares { - copy(share.GetNamespace(shr), namespace) + copy(shr.Namespace().Bytes(), namespace.Bytes()) } eds, err := AddShares(ctx, shares, bServ) @@ -239,8 +247,8 @@ func TestCollectLeavesByNamespace_AbsentNamespaceId(t *testing.T) { t.Cleanup(cancel) bServ := NewMemBlockservice() - shares := sharetest.RandShares(t, 1024) - + shares, err := libshare.RandShares(1024) + require.NoError(t, err) // set all shares to the same namespace namespaces, err := randomNamespaces(5) require.NoError(t, err) @@ -253,16 +261,16 @@ func TestCollectLeavesByNamespace_AbsentNamespaceId(t *testing.T) { secondNamespaceFrom := mrand.Intn(len(shares)-2) + 1 for i, shr := range shares { if i < secondNamespaceFrom { - copy(share.GetNamespace(shr), minIncluded) + copy(shr.Namespace().Bytes(), minIncluded.Bytes()) continue } - copy(share.GetNamespace(shr), maxIncluded) + copy(shr.Namespace().Bytes(), maxIncluded.Bytes()) } tests := []struct { name string - data []share.Share - missingNamespace share.Namespace + data []libshare.Share + missingNamespace libshare.Namespace isAbsence bool }{ {name: "Namespace less than the minimum namespace in data", data: shares, missingNamespace: minNamespace}, @@ -284,10 +292,10 @@ func TestCollectLeavesByNamespace_MultipleRowsContainingSameNamespaceId(t *testi t.Cleanup(cancel) bServ := NewMemBlockservice() - shares := sharetest.RandShares(t, 16) - + shares, err := libshare.RandShares(16) + require.NoError(t, err) // set all shares to the same namespace and data but the last one - namespace := share.GetNamespace(shares[0]) + namespace := shares[0].Namespace() commonNamespaceData := shares[0] for i, nspace := range shares { @@ -295,7 +303,7 @@ func TestCollectLeavesByNamespace_MultipleRowsContainingSameNamespaceId(t *testi break } - copy(nspace, commonNamespaceData) + copy(nspace.ToBytes(), commonNamespaceData.ToBytes()) } eds, err := AddShares(ctx, shares, bServ) @@ -316,7 +324,9 @@ func TestCollectLeavesByNamespace_MultipleRowsContainingSameNamespaceId(t *testi for _, node := range leaves { // test that the data returned by collectLeavesByNamespace for nid // matches the commonNamespaceData that was copied across almost all data - assert.Equal(t, commonNamespaceData, share.GetData(node.RawData())) + sh, err := libshare.NewShare(node.RawData()[libshare.NamespaceSize:]) + require.NoError(t, err) + assert.Equal(t, commonNamespaceData, *sh) } } } @@ -326,12 +336,18 @@ func TestGetSharesWithProofsByNamespace(t *testing.T) { t.Cleanup(cancel) bServ := NewMemBlockservice() + sh0, err := libshare.RandShares(4) + require.NoError(t, err) + sh1, err := libshare.RandShares(16) + require.NoError(t, err) + sh2, err := libshare.RandShares(64) + require.NoError(t, err) tests := []struct { - rawData []share.Share + rawData []libshare.Share }{ - {rawData: sharetest.RandShares(t, 4)}, - {rawData: sharetest.RandShares(t, 16)}, - {rawData: sharetest.RandShares(t, 64)}, + {rawData: sh0}, + {rawData: sh1}, + {rawData: sh2}, } for i, tt := range tests { @@ -346,7 +362,7 @@ func TestGetSharesWithProofsByNamespace(t *testing.T) { } expected := tt.rawData[from] - namespace := share.GetNamespace(expected) + namespace := expected.Namespace() // change rawData to contain several shares with same namespace for i := from; i <= to; i++ { @@ -357,13 +373,14 @@ func TestGetSharesWithProofsByNamespace(t *testing.T) { eds, err := AddShares(ctx, tt.rawData, bServ) require.NoError(t, err) - var shares []share.Share + var shares []libshare.Share rowRoots, err := eds.RowRoots() require.NoError(t, err) for _, row := range rowRoots { - rcid := MustCidFromNamespacedSha256(row) - rowShares, proof, err := GetSharesByNamespace(ctx, bServ, rcid, namespace, len(rowRoots)) - if namespace.IsOutsideRange(row, row) { + rowShares, proof, err := GetSharesByNamespace(ctx, bServ, row, namespace, len(rowRoots)) + outside, outsideErr := share.IsOutsideRange(namespace, row, row) + require.NoError(t, outsideErr) + if outside { require.ErrorIs(t, err, ErrNamespaceOutsideRange) continue } @@ -376,23 +393,23 @@ func TestGetSharesWithProofsByNamespace(t *testing.T) { // construct nodes from shares by prepending namespace var leaves [][]byte for _, shr := range rowShares { - leaves = append(leaves, append(share.GetNamespace(shr), shr...)) + leaves = append(leaves, append(shr.Namespace().Bytes(), shr.ToBytes()...)) } // verify namespace verified := proof.VerifyNamespace( share.NewSHA256Hasher(), - namespace.ToNMT(), + namespace.Bytes(), leaves, - NamespacedSha256FromCID(rcid)) + row) require.True(t, verified) // verify inclusion verified = proof.VerifyInclusion( share.NewSHA256Hasher(), - namespace.ToNMT(), - rowShares, - NamespacedSha256FromCID(rcid)) + namespace.Bytes(), + libshare.ToBytes(rowShares), + row) require.True(t, verified) } } @@ -426,7 +443,10 @@ func TestBatchSize(t *testing.T) { bs := NewMemBlockservice() randEds := edstest.RandEDS(t, tt.origWidth) - _, err := AddShares(ctx, randEds.FlattenedODS(), bs) + + shrs, err := libshare.FromBytes(randEds.FlattenedODS()) + require.NoError(t, err) + _, err = AddShares(ctx, shrs, bs) require.NoError(t, err) out, err := bs.Blockstore().AllKeysChan(ctx) @@ -447,7 +467,7 @@ func assertNoRowContainsNID( t *testing.T, bServ blockservice.BlockService, eds *rsmt2d.ExtendedDataSquare, - namespace share.Namespace, + namespace libshare.Namespace, isAbsent bool, ) { rowRoots, err := eds.RowRoots() @@ -462,16 +482,15 @@ func assertNoRowContainsNID( // for each row root cid check if the min namespace exists var absentCount, foundAbsenceRows int for _, rowRoot := range rowRoots { - var outsideRange bool - if !namespace.IsOutsideRange(rowRoot, rowRoot) { + outsideRange, err := share.IsOutsideRange(namespace, rowRoot, rowRoot) + require.NoError(t, err) + if !outsideRange { // namespace does belong to namespace range of the row absentCount++ - } else { - outsideRange = true } data := NewNamespaceData(rowRootCount, namespace, WithProofs()) rootCID := MustCidFromNamespacedSha256(rowRoot) - err := data.CollectLeavesByNamespace(ctx, bServ, rootCID) + err = data.CollectLeavesByNamespace(ctx, bServ, rootCID) if outsideRange { require.ErrorIs(t, err, ErrNamespaceOutsideRange) continue @@ -480,7 +499,7 @@ func assertNoRowContainsNID( // if no error returned, check absence proof foundAbsenceRows++ - verified := data.Proof().VerifyNamespace(share.NewSHA256Hasher(), namespace.ToNMT(), nil, rowRoot) + verified := data.Proof().VerifyNamespace(share.NewSHA256Hasher(), namespace.Bytes(), nil, rowRoot) require.True(t, verified) } @@ -491,11 +510,11 @@ func assertNoRowContainsNID( } } -func randomNamespaces(total int) ([]share.Namespace, error) { - namespaces := make([]share.Namespace, total) +func randomNamespaces(total int) ([]libshare.Namespace, error) { + namespaces := make([]libshare.Namespace, total) for i := range namespaces { - namespaces[i] = sharetest.RandV0Namespace() + namespaces[i] = libshare.RandomNamespace() } - sort.Slice(namespaces, func(i, j int) bool { return bytes.Compare(namespaces[i], namespaces[j]) < 0 }) + sort.Slice(namespaces, func(i, j int) bool { return namespaces[i].IsLessThan(namespaces[j]) }) return namespaces, nil } diff --git a/share/ipld/namespace_data.go b/share/ipld/namespace_data.go index 1d8b71ec24..2afc13c05a 100644 --- a/share/ipld/namespace_data.go +++ b/share/ipld/namespace_data.go @@ -11,13 +11,14 @@ import ( "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" ) -var ErrNamespaceOutsideRange = errors.New("share/ipld: " + - "target namespace is outside of namespace range for the given root") +var ErrNamespaceOutsideRange = shwap.ErrNamespaceOutsideRange // Option is the functional option that is applied to the NamespaceData instance // to configure data that needs to be stored. @@ -46,13 +47,13 @@ type NamespaceData struct { bounds fetchedBounds maxShares int - namespace share.Namespace + namespace libshare.Namespace isAbsentNamespace atomic.Bool absenceProofLeaf ipld.Node } -func NewNamespaceData(maxShares int, namespace share.Namespace, options ...Option) *NamespaceData { +func NewNamespaceData(maxShares int, namespace libshare.Namespace, options ...Option) *NamespaceData { data := &NamespaceData{ maxShares: maxShares, namespace: namespace, @@ -71,7 +72,7 @@ func NewNamespaceData(maxShares int, namespace share.Namespace, options ...Optio } func (n *NamespaceData) validate(rootCid cid.Cid) error { - if err := n.namespace.Validate(); err != nil { + if err := n.namespace.ValidateForData(); err != nil { return err } @@ -80,7 +81,11 @@ func (n *NamespaceData) validate(rootCid cid.Cid) error { } root := NamespacedSha256FromCID(rootCid) - if n.namespace.IsOutsideRange(root, root) { + outside, err := share.IsOutsideRange(n.namespace, root, root) + if err != nil { + return err + } + if outside { return ErrNamespaceOutsideRange } return nil @@ -283,18 +288,31 @@ func (n *NamespaceData) collectNDWithProofs(j job, links []*ipld.Link) []job { rightLink := NamespacedSha256FromCID(rightCid) var nextJobs []job + outside, err := share.IsOutsideRange(n.namespace, leftLink, rightLink) + if err != nil { + log.Fatalf("invalid hashes provided: %v", err) + } // check if target namespace is outside of boundaries of both links - if n.namespace.IsOutsideRange(leftLink, rightLink) { + if outside { log.Fatalf("target namespace outside of boundaries of links at depth: %v", j.depth) } - if !n.namespace.IsAboveMax(leftLink) { + above, err := share.IsAboveMax(n.namespace, leftLink) + if err != nil { + log.Fatal(err) + } + + if !above { // namespace is within the range of left link nextJobs = append(nextJobs, j.next(left, leftCid, false)) } else { // proof is on the left side, if the namespace is on the right side of the range of left link n.addProof(left, leftCid, j.depth) - if n.namespace.IsBelowMin(rightLink) { + below, err := share.IsBelowMin(n.namespace, rightLink) + if err != nil { + log.Fatal(err) + } + if below { // namespace is not included in either links, convert to absence collector n.isAbsentNamespace.Store(true) nextJobs = append(nextJobs, j.next(right, rightCid, true)) @@ -302,7 +320,12 @@ func (n *NamespaceData) collectNDWithProofs(j job, links []*ipld.Link) []job { } } - if !n.namespace.IsBelowMin(rightLink) { + below, err := share.IsBelowMin(n.namespace, rightLink) + if err != nil { + log.Fatal(err) + } + + if !below { // namespace is within the range of right link nextJobs = append(nextJobs, j.next(right, rightCid, false)) } else { diff --git a/share/ipld/nmt.go b/share/ipld/nmt.go index 83c94bde12..702e90a9c2 100644 --- a/share/ipld/nmt.go +++ b/share/ipld/nmt.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "hash" - "math/rand" "github.com/ipfs/boxo/blockservice" blocks "github.com/ipfs/go-block-format" @@ -16,8 +15,7 @@ import ( mh "github.com/multiformats/go-multihash" mhcore "github.com/multiformats/go-multihash/core" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/pkg/da" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/celestia-node/share" @@ -37,13 +35,13 @@ const ( sha256NamespaceFlagged = 0x7701 // NmtHashSize is the size of a digest created by an NMT in bytes. - NmtHashSize = 2*share.NamespaceSize + sha256.Size + NmtHashSize = 2*libshare.NamespaceSize + sha256.Size // InnerNodeSize is the size of data in inner nodes. InnerNodeSize = NmtHashSize * 2 // LeafNodeSize is the size of data in leaf nodes. - LeafNodeSize = share.NamespaceSize + appconsts.ShareSize + LeafNodeSize = libshare.NamespaceSize + libshare.ShareSize // cidPrefixSize is the size of the prepended buffer of the CID encoding // for NamespacedSha256. For more information, see: @@ -59,7 +57,7 @@ const ( func init() { // required for Bitswap to hash and verify inbound data correctly mhcore.Register(sha256NamespaceFlagged, func() hash.Hash { - nh := nmt.NewNmtHasher(share.NewSHA256Hasher(), share.NamespaceSize, true) + nh := nmt.NewNmtHasher(share.NewSHA256Hasher(), libshare.NamespaceSize, true) nh.Reset() return nh }) @@ -156,16 +154,6 @@ func MustCidFromNamespacedSha256(hash []byte) cid.Cid { return cidFromHash } -// Translate transforms square coordinates into IPLD NMT tree path to a leaf node. -// It also adds randomization to evenly spread fetching from Rows and Columns. -func Translate(dah *da.DataAvailabilityHeader, row, col int) (cid.Cid, int) { - if rand.Intn(2) == 0 { //nolint:gosec - return MustCidFromNamespacedSha256(dah.ColumnRoots[col]), row - } - - return MustCidFromNamespacedSha256(dah.RowRoots[row]), col -} - // NamespacedSha256FromCID derives the Namespaced hash from the given CID. func NamespacedSha256FromCID(cid cid.Cid) []byte { return cid.Hash()[cidPrefixSize:] diff --git a/share/ipld/nmt_adder.go b/share/ipld/nmt_adder.go index 7ce52859b2..2ac87f69a4 100644 --- a/share/ipld/nmt_adder.go +++ b/share/ipld/nmt_adder.go @@ -103,13 +103,15 @@ func BatchSize(squareSize int) int { // ProofsAdder is used to collect proof nodes, while traversing merkle tree type ProofsAdder struct { - lock sync.RWMutex - proofs map[cid.Cid][]byte + lock sync.RWMutex + collectShares bool + proofs map[cid.Cid][]byte } // NewProofsAdder creates new instance of ProofsAdder. -func NewProofsAdder(squareSize int) *ProofsAdder { +func NewProofsAdder(squareSize int, collectShares bool) *ProofsAdder { return &ProofsAdder{ + collectShares: collectShares, // preallocate map to fit all inner nodes for given square size proofs: make(map[cid.Cid][]byte, innerNodesAmount(squareSize)), } @@ -156,7 +158,7 @@ func (a *ProofsAdder) VisitFn() nmt.NodeVisitorFn { if len(a.proofs) > 0 { return nil } - return a.visitInnerNodes + return a.visitNodes } // Purge removed proofs from ProofsAdder allowing GC to collect the memory @@ -171,10 +173,13 @@ func (a *ProofsAdder) Purge() { a.proofs = nil } -func (a *ProofsAdder) visitInnerNodes(hash []byte, children ...[]byte) { +func (a *ProofsAdder) visitNodes(hash []byte, children ...[]byte) { switch len(children) { case 1: - break + if a.collectShares { + id := MustCidFromNamespacedSha256(hash) + a.addProof(id, children[0]) + } case 2: id := MustCidFromNamespacedSha256(hash) a.addProof(id, append(children[0], children[1]...)) @@ -191,5 +196,5 @@ func (a *ProofsAdder) addProof(id cid.Cid, proof []byte) { // innerNodesAmount return amount of inner nodes in eds with given size func innerNodesAmount(squareSize int) int { - return 2 * (squareSize - 1) * squareSize + return 2*squareSize - 1 } diff --git a/share/ipld/nmt_test.go b/share/ipld/nmt_test.go index 76d67bd2a8..040c68ec1d 100644 --- a/share/ipld/nmt_test.go +++ b/share/ipld/nmt_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/pkg/da" "github.com/celestiaorg/rsmt2d" + "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds/edstest" ) @@ -26,10 +26,10 @@ func TestNamespaceFromCID(t *testing.T) { for i, tt := range tests { t.Run(strconv.Itoa(i), func(t *testing.T) { - dah, err := da.NewDataAvailabilityHeader(tt.eds) + roots, err := share.NewAxisRoots(tt.eds) require.NoError(t, err) // check to make sure NamespacedHash is correctly derived from CID - for _, row := range dah.RowRoots { + for _, row := range roots.RowRoots { c, err := CidFromNamespacedSha256(row) require.NoError(t, err) diff --git a/share/ipld/proof_collector.go b/share/ipld/proof_collector.go index 937c8b416e..856474e93a 100644 --- a/share/ipld/proof_collector.go +++ b/share/ipld/proof_collector.go @@ -1,7 +1,7 @@ package ipld import ( - "math" + "math/bits" "github.com/ipfs/go-cid" ) @@ -14,7 +14,10 @@ type proofCollector struct { func newProofCollector(maxShares int) *proofCollector { // maximum possible amount of required proofs from each side is equal to tree height. - height := int(math.Log2(float64(maxShares))) + 1 + height := bits.Len64(uint64(maxShares)) + if maxShares > 0 { + height++ + } return &proofCollector{ left: make([]cid.Cid, height), right: make([]cid.Cid, height), diff --git a/share/ipld/proofs.go b/share/ipld/proofs.go new file mode 100644 index 0000000000..286e817d95 --- /dev/null +++ b/share/ipld/proofs.go @@ -0,0 +1,74 @@ +package ipld + +import ( + "context" + "math" + + "github.com/ipfs/boxo/blockservice" + "github.com/ipfs/go-cid" + + "github.com/celestiaorg/nmt" +) + +// GetProof fetches and returns the leaf's Merkle Proof. +// It walks down the IPLD NMT tree until it reaches the leaf and returns collected proof +func GetProof( + ctx context.Context, + bGetter blockservice.BlockGetter, + root []byte, + shareIdx, + total int, +) (nmt.Proof, error) { + rootCid := MustCidFromNamespacedSha256(root) + proofPath := make([]cid.Cid, 0, int(math.Sqrt(float64(total)))) + proofPath, err := getProof(ctx, bGetter, rootCid, proofPath, shareIdx, total) + if err != nil { + return nmt.Proof{}, err + } + + rangeProofs := make([][]byte, 0, len(proofPath)) + for i := len(proofPath) - 1; i >= 0; i-- { + node := NamespacedSha256FromCID(proofPath[i]) + rangeProofs = append(rangeProofs, node) + } + + return nmt.NewInclusionProof(shareIdx, shareIdx+1, rangeProofs, true), nil +} + +func getProof( + ctx context.Context, + bGetter blockservice.BlockGetter, + root cid.Cid, + proof []cid.Cid, + leaf, total int, +) ([]cid.Cid, error) { + // request the node + nd, err := GetNode(ctx, bGetter, root) + if err != nil { + return nil, err + } + // look for links + lnks := nd.Links() + if len(lnks) == 0 { + p := make([]cid.Cid, len(proof)) + copy(p, proof) + return p, nil + } + + // route walk to appropriate children + total /= 2 // as we are using binary tree, every step decreases total leaves in a half + if leaf < total { + root = lnks[0].Cid // if target leave on the left, go with walk down the first children + proof = append(proof, lnks[1].Cid) + } else { + root, leaf = lnks[1].Cid, leaf-total // otherwise go down the second + proof, err = getProof(ctx, bGetter, root, proof, leaf, total) + if err != nil { + return nil, err + } + return append(proof, lnks[0].Cid), nil + } + + // recursively walk down through selected children + return getProof(ctx, bGetter, root, proof, leaf, total) +} diff --git a/share/ipld/proofs_test.go b/share/ipld/proofs_test.go new file mode 100644 index 0000000000..a17c14697d --- /dev/null +++ b/share/ipld/proofs_test.go @@ -0,0 +1,68 @@ +package ipld + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestGetProof(t *testing.T) { + const width = 8 + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + bServ := NewMemBlockservice() + + shares, err := libshare.RandShares(width * width) + require.NoError(t, err) + in, err := AddShares(ctx, shares, bServ) + require.NoError(t, err) + + axisRoots, err := share.NewAxisRoots(in) + require.NoError(t, err) + + for _, proofType := range []rsmt2d.Axis{rsmt2d.Row, rsmt2d.Col} { + var roots [][]byte + switch proofType { + case rsmt2d.Row: + roots = axisRoots.RowRoots + case rsmt2d.Col: + roots = axisRoots.ColumnRoots + } + for axisIdx := 0; axisIdx < width*2; axisIdx++ { + root := roots[axisIdx] + for shrIdx := 0; shrIdx < width*2; shrIdx++ { + proof, err := GetProof(ctx, bServ, root, shrIdx, int(in.Width())) + require.NoError(t, err) + rootCid := MustCidFromNamespacedSha256(root) + node, err := GetLeaf(ctx, bServ, rootCid, shrIdx, int(in.Width())) + require.NoError(t, err) + + sh, err := libshare.NewShare(node.RawData()[libshare.NamespaceSize:]) + require.NoError(t, err) + sample := shwap.Sample{ + Share: *sh, + Proof: &proof, + ProofType: proofType, + } + var rowIdx, colIdx int + switch proofType { + case rsmt2d.Row: + rowIdx, colIdx = axisIdx, shrIdx + case rsmt2d.Col: + rowIdx, colIdx = shrIdx, axisIdx + } + err = sample.Verify(axisRoots, rowIdx, colIdx) + require.NoError(t, err) + } + } + } +} diff --git a/share/ipld/test_helpers.go b/share/ipld/test_helpers.go deleted file mode 100644 index e3456c0b25..0000000000 --- a/share/ipld/test_helpers.go +++ /dev/null @@ -1,18 +0,0 @@ -package ipld - -import ( - "crypto/rand" - "testing" - - "github.com/ipfs/go-cid" - "github.com/stretchr/testify/require" -) - -func RandNamespacedCID(t *testing.T) cid.Cid { - raw := make([]byte, NmtHashSize) - _, err := rand.Read(raw) - require.NoError(t, err) - id, err := CidFromNamespacedSha256(raw) - require.NoError(t, err) - return id -} diff --git a/share/ipld/utils.go b/share/ipld/utils.go deleted file mode 100644 index d3e987e7f3..0000000000 --- a/share/ipld/utils.go +++ /dev/null @@ -1,18 +0,0 @@ -package ipld - -import ( - "github.com/ipfs/go-cid" - - "github.com/celestiaorg/celestia-node/share" -) - -// FilterRootByNamespace returns the row roots from the given share.Root that contain the namespace. -func FilterRootByNamespace(root *share.Root, namespace share.Namespace) []cid.Cid { - rowRootCIDs := make([]cid.Cid, 0, len(root.RowRoots)) - for _, row := range root.RowRoots { - if !namespace.IsOutsideRange(row, row) { - rowRootCIDs = append(rowRootCIDs, MustCidFromNamespacedSha256(row)) - } - } - return rowRootCIDs -} diff --git a/share/mocks/getter.go b/share/mocks/getter.go deleted file mode 100644 index 738e2b246c..0000000000 --- a/share/mocks/getter.go +++ /dev/null @@ -1,83 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/celestiaorg/celestia-node/share (interfaces: Getter) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - context "context" - reflect "reflect" - - header "github.com/celestiaorg/celestia-node/header" - share "github.com/celestiaorg/celestia-node/share" - rsmt2d "github.com/celestiaorg/rsmt2d" - gomock "github.com/golang/mock/gomock" -) - -// MockGetter is a mock of Getter interface. -type MockGetter struct { - ctrl *gomock.Controller - recorder *MockGetterMockRecorder -} - -// MockGetterMockRecorder is the mock recorder for MockGetter. -type MockGetterMockRecorder struct { - mock *MockGetter -} - -// NewMockGetter creates a new mock instance. -func NewMockGetter(ctrl *gomock.Controller) *MockGetter { - mock := &MockGetter{ctrl: ctrl} - mock.recorder = &MockGetterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockGetter) EXPECT() *MockGetterMockRecorder { - return m.recorder -} - -// GetEDS mocks base method. -func (m *MockGetter) GetEDS(arg0 context.Context, arg1 *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetEDS", arg0, arg1) - ret0, _ := ret[0].(*rsmt2d.ExtendedDataSquare) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetEDS indicates an expected call of GetEDS. -func (mr *MockGetterMockRecorder) GetEDS(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEDS", reflect.TypeOf((*MockGetter)(nil).GetEDS), arg0, arg1) -} - -// GetShare mocks base method. -func (m *MockGetter) GetShare(arg0 context.Context, arg1 *header.ExtendedHeader, arg2, arg3 int) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetShare", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetShare indicates an expected call of GetShare. -func (mr *MockGetterMockRecorder) GetShare(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShare", reflect.TypeOf((*MockGetter)(nil).GetShare), arg0, arg1, arg2, arg3) -} - -// GetSharesByNamespace mocks base method. -func (m *MockGetter) GetSharesByNamespace(arg0 context.Context, arg1 *header.ExtendedHeader, arg2 share.Namespace) (share.NamespacedShares, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSharesByNamespace", arg0, arg1, arg2) - ret0, _ := ret[0].(share.NamespacedShares) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSharesByNamespace indicates an expected call of GetSharesByNamespace. -func (mr *MockGetterMockRecorder) GetSharesByNamespace(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSharesByNamespace", reflect.TypeOf((*MockGetter)(nil).GetSharesByNamespace), arg0, arg1, arg2) -} diff --git a/share/namespace.go b/share/namespace.go deleted file mode 100644 index 24188ecb9b..0000000000 --- a/share/namespace.go +++ /dev/null @@ -1,184 +0,0 @@ -package share - -import ( - "bytes" - "encoding/hex" - "fmt" - - appns "github.com/celestiaorg/go-square/namespace" - "github.com/celestiaorg/nmt/namespace" -) - -// NamespaceSize is a system-wide size for NMT namespaces. -const NamespaceSize = appns.NamespaceSize - -// Various reserved namespaces. -var ( - // MaxPrimaryReservedNamespace is the highest primary reserved namespace. - // Namespaces lower than this are reserved for protocol use. - MaxPrimaryReservedNamespace = Namespace(appns.MaxPrimaryReservedNamespace.Bytes()) - // MinSecondaryReservedNamespace is the lowest secondary reserved namespace - // reserved for protocol use. Namespaces higher than this are reserved for - // protocol use. - MinSecondaryReservedNamespace = Namespace(appns.MinSecondaryReservedNamespace.Bytes()) - ParitySharesNamespace = Namespace(appns.ParitySharesNamespace.Bytes()) - TailPaddingNamespace = Namespace(appns.TailPaddingNamespace.Bytes()) - PrimaryReservedPaddingNamespace = Namespace(appns.PrimaryReservedPaddingNamespace.Bytes()) - TxNamespace = Namespace(appns.TxNamespace.Bytes()) - PayForBlobNamespace = Namespace(appns.PayForBlobNamespace.Bytes()) - ISRNamespace = Namespace(appns.IntermediateStateRootsNamespace.Bytes()) -) - -// Namespace represents namespace of a Share. -// Consists of version byte and namespace ID. -type Namespace []byte - -// NewBlobNamespaceV0 takes a variable size byte slice and creates a valid version 0 Blob Namespace. -// The byte slice must be <= 10 bytes. -// If it is less than 10 bytes, it will be left padded to size 10 with 0s. -// Use predefined namespaces above, if non-blob namespace is needed. -func NewBlobNamespaceV0(id []byte) (Namespace, error) { - if len(id) == 0 || len(id) > appns.NamespaceVersionZeroIDSize { - return nil, fmt.Errorf( - "namespace id must be > 0 && <= %d, but it was %d bytes", appns.NamespaceVersionZeroIDSize, len(id)) - } - - n := make(Namespace, NamespaceSize) - // version and zero padding are already set as zero, - // so simply copying subNID to the end is enough to comply the V0 spec - copy(n[len(n)-len(id):], id) - return n, n.ValidateForBlob() -} - -// NamespaceFromBytes converts bytes into Namespace and validates it. -func NamespaceFromBytes(b []byte) (Namespace, error) { - n := Namespace(b) - return n, n.Validate() -} - -// Version reports version of the Namespace. -func (n Namespace) Version() byte { - return n[appns.NamespaceVersionSize-1] -} - -// ID reports ID of the Namespace. -func (n Namespace) ID() namespace.ID { - return namespace.ID(n[appns.NamespaceVersionSize:]) -} - -// ToNMT converts the whole Namespace(both Version and ID parts) into NMT's namespace.ID -// NOTE: Once https://github.com/celestiaorg/nmt/issues/206 is closed Namespace should become NNT's -// type. -func (n Namespace) ToNMT() namespace.ID { - return namespace.ID(n) -} - -// ToAppNamespace converts the Namespace to App's definition of Namespace. -// TODO: Unify types between node and app -func (n Namespace) ToAppNamespace() appns.Namespace { - return appns.Namespace{Version: n.Version(), ID: n.ID()} -} - -// Len reports the total length of the namespace. -func (n Namespace) Len() int { - return len(n) -} - -// String stringifies the Namespace. -func (n Namespace) String() string { - return hex.EncodeToString(n) -} - -// Equals compares two Namespaces. -func (n Namespace) Equals(target Namespace) bool { - return bytes.Equal(n, target) -} - -// Validate checks if the namespace is correct. -func (n Namespace) Validate() error { - if n.Len() != NamespaceSize { - return fmt.Errorf("invalid namespace length: expected %d, got %d", NamespaceSize, n.Len()) - } - if n.Version() != appns.NamespaceVersionZero && n.Version() != appns.NamespaceVersionMax { - return fmt.Errorf("invalid namespace version %v", n.Version()) - } - if len(n.ID()) != appns.NamespaceIDSize { - return fmt.Errorf("invalid namespace id length: expected %d, got %d", appns.NamespaceIDSize, n.ID().Size()) - } - if n.Version() == appns.NamespaceVersionZero && !bytes.HasPrefix(n.ID(), appns.NamespaceVersionZeroPrefix) { - return fmt.Errorf("invalid namespace id: expect %d leading zeroes", len(appns.NamespaceVersionZeroPrefix)) - } - return nil -} - -// ValidateForData checks if the Namespace is of real/useful data. -func (n Namespace) ValidateForData() error { - if err := n.Validate(); err != nil { - return err - } - if n.Equals(ParitySharesNamespace) || n.Equals(TailPaddingNamespace) { - return fmt.Errorf("invalid data namespace(%s): parity and tail padding namespace are forbidden", n) - } - if n.Version() != appns.NamespaceVersionZero { - return fmt.Errorf("invalid data namespace(%s): only version 0 is supported", n) - } - return nil -} - -// ValidateForBlob checks if the Namespace is valid blob namespace. -func (n Namespace) ValidateForBlob() error { - if err := n.ValidateForData(); err != nil { - return err - } - if bytes.Compare(n, MaxPrimaryReservedNamespace) < 1 { - return fmt.Errorf("invalid blob namespace(%s): reserved namespaces are forbidden", n) - } - if bytes.Compare(n, MinSecondaryReservedNamespace) > -1 { - return fmt.Errorf("invalid blob namespace(%s): reserved namespaces are forbidden", n) - } - return nil -} - -// IsAboveMax checks if the namespace is above the maximum namespace of the given hash. -func (n Namespace) IsAboveMax(nodeHash []byte) bool { - return !n.IsLessOrEqual(nodeHash[n.Len() : n.Len()*2]) -} - -// IsBelowMin checks if the target namespace is below the minimum namespace of the given hash. -func (n Namespace) IsBelowMin(nodeHash []byte) bool { - return n.IsLess(nodeHash[:n.Len()]) -} - -// IsOutsideRange checks if the namespace is outside the min-max range of the given hashes. -func (n Namespace) IsOutsideRange(leftNodeHash, rightNodeHash []byte) bool { - return n.IsBelowMin(leftNodeHash) || n.IsAboveMax(rightNodeHash) -} - -// Repeat copies the Namespace t times. -func (n Namespace) Repeat(t int) []Namespace { - ns := make([]Namespace, t) - for i := 0; i < t; i++ { - ns[i] = n - } - return ns -} - -// IsLess reports if the Namespace is less than the target. -func (n Namespace) IsLess(target Namespace) bool { - return bytes.Compare(n, target) == -1 -} - -// IsLessOrEqual reports if the Namespace is less than the target. -func (n Namespace) IsLessOrEqual(target Namespace) bool { - return bytes.Compare(n, target) < 1 -} - -// IsGreater reports if the Namespace is greater than the target. -func (n Namespace) IsGreater(target Namespace) bool { - return bytes.Compare(n, target) == 1 -} - -// IsGreaterOrEqualThan reports if the Namespace is greater or equal than the target. -func (n Namespace) IsGreaterOrEqualThan(target Namespace) bool { - return bytes.Compare(n, target) > -1 -} diff --git a/share/namespace_test.go b/share/namespace_test.go deleted file mode 100644 index e6007c245e..0000000000 --- a/share/namespace_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package share - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - appns "github.com/celestiaorg/go-square/namespace" -) - -var ( - validID = append( - appns.NamespaceVersionZeroPrefix, - bytes.Repeat([]byte{1}, appns.NamespaceVersionZeroIDSize)..., - ) - tooShortID = append(appns.NamespaceVersionZeroPrefix, []byte{1}...) - tooLongID = append(appns.NamespaceVersionZeroPrefix, bytes.Repeat([]byte{1}, NamespaceSize)...) - invalidPrefixID = bytes.Repeat([]byte{1}, NamespaceSize) -) - -func TestNewNamespaceV0(t *testing.T) { - type testCase struct { - name string - subNid []byte - expected Namespace - wantErr bool - } - testCases := []testCase{ - { - name: "8 byte id, gets left padded", - subNid: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, - expected: Namespace{ - 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // filled zeros - 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, - }, // id with left padding - wantErr: false, - }, - { - name: "10 byte id, no padding", - subNid: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x9, 0x10}, - expected: Namespace{ - 0x0, // version - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // filled zeros - 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, - }, // id - wantErr: false, - }, - { - name: "11 byte id", - subNid: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x9, 0x10, 0x11}, - expected: []byte{}, - wantErr: true, - }, - { - name: "nil id", - subNid: nil, - expected: []byte{}, - wantErr: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - got, err := NewBlobNamespaceV0(tc.subNid) - if tc.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tc.expected, got) - }) - } -} - -func TestFrom(t *testing.T) { - type testCase struct { - name string - bytes []byte - wantErr bool - want Namespace - } - validNamespace := []byte{} - validNamespace = append(validNamespace, appns.NamespaceVersionZero) - validNamespace = append(validNamespace, appns.NamespaceVersionZeroPrefix...) - validNamespace = append(validNamespace, bytes.Repeat([]byte{0x1}, appns.NamespaceVersionZeroIDSize)...) - parityNamespace := bytes.Repeat([]byte{0xFF}, NamespaceSize) - - testCases := []testCase{ - { - name: "valid namespace", - bytes: validNamespace, - wantErr: false, - want: append([]byte{appns.NamespaceVersionZero}, validID...), - }, - { - name: "parity namespace", - bytes: parityNamespace, - wantErr: false, - want: append([]byte{appns.NamespaceVersionMax}, bytes.Repeat([]byte{0xFF}, appns.NamespaceIDSize)...), - }, - { - name: "unsupported version", - bytes: append([]byte{1}, append( - appns.NamespaceVersionZeroPrefix, - bytes.Repeat([]byte{1}, NamespaceSize-len(appns.NamespaceVersionZeroPrefix))..., - )...), - wantErr: true, - }, - { - name: "unsupported id: too short", - bytes: append([]byte{appns.NamespaceVersionZero}, tooShortID...), - wantErr: true, - }, - { - name: "unsupported id: too long", - bytes: append([]byte{appns.NamespaceVersionZero}, tooLongID...), - wantErr: true, - }, - { - name: "unsupported id: invalid prefix", - bytes: append([]byte{appns.NamespaceVersionZero}, invalidPrefixID...), - wantErr: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - got, err := NamespaceFromBytes(tc.bytes) - if tc.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tc.want, got) - }) - } -} - -func TestValidateForBlob(t *testing.T) { - type testCase struct { - name string - ns Namespace - wantErr bool - } - - validNamespace, err := NewBlobNamespaceV0(bytes.Repeat([]byte{0x1}, appns.NamespaceVersionZeroIDSize)) - require.NoError(t, err) - - testCases := []testCase{ - { - name: "valid blob namespace", - ns: validNamespace, - wantErr: false, - }, - { - name: "invalid blob namespace: parity shares namespace", - ns: ParitySharesNamespace, - wantErr: true, - }, - { - name: "invalid blob namespace: tail padding namespace", - ns: TailPaddingNamespace, - wantErr: true, - }, - { - name: "invalid blob namespace: tx namespace", - ns: TxNamespace, - wantErr: true, - }, - { - name: "invalid blob namespace: namespace version max", - ns: append([]byte{appns.NamespaceVersionMax}, bytes.Repeat([]byte{0x0}, appns.NamespaceIDSize)...), - wantErr: true, - }, - { - name: "invalid blob namespace: primary reserved namespace", - ns: primaryReservedNamespace(0x10), - wantErr: true, - }, - { - name: "invalid blob namespace: secondary reserved namespace", - ns: secondaryReservedNamespace(0x10), - wantErr: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := tc.ns.ValidateForBlob() - - if tc.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - }) - } -} - -func primaryReservedNamespace(lastByte byte) Namespace { - result := make([]byte, NamespaceSize) - result = append(result, appns.NamespaceVersionZero) - result = append(result, appns.NamespaceVersionZeroPrefix...) - result = append(result, bytes.Repeat([]byte{0x0}, appns.NamespaceVersionZeroIDSize-1)...) - result = append(result, lastByte) - return result -} - -func secondaryReservedNamespace(lastByte byte) Namespace { - result := make([]byte, NamespaceSize) - result = append(result, appns.NamespaceVersionMax) - result = append(result, bytes.Repeat([]byte{0xFF}, appns.NamespaceIDSize-1)...) - result = append(result, lastByte) - return result -} diff --git a/share/p2p/shrexeds/pb/extended_data_square.pb.go b/share/p2p/shrexeds/pb/extended_data_square.pb.go deleted file mode 100644 index ed1a96ae3b..0000000000 --- a/share/p2p/shrexeds/pb/extended_data_square.pb.go +++ /dev/null @@ -1,509 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: share/p2p/shrexeds/pb/extended_data_square.proto - -package extended_data_square - -import ( - fmt "fmt" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -type Status int32 - -const ( - Status_INVALID Status = 0 - Status_OK Status = 1 - Status_NOT_FOUND Status = 2 - Status_INTERNAL Status = 3 -) - -var Status_name = map[int32]string{ - 0: "INVALID", - 1: "OK", - 2: "NOT_FOUND", - 3: "INTERNAL", -} - -var Status_value = map[string]int32{ - "INVALID": 0, - "OK": 1, - "NOT_FOUND": 2, - "INTERNAL": 3, -} - -func (x Status) String() string { - return proto.EnumName(Status_name, int32(x)) -} - -func (Status) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_49d42aa96098056e, []int{0} -} - -type EDSRequest struct { - Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` -} - -func (m *EDSRequest) Reset() { *m = EDSRequest{} } -func (m *EDSRequest) String() string { return proto.CompactTextString(m) } -func (*EDSRequest) ProtoMessage() {} -func (*EDSRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_49d42aa96098056e, []int{0} -} -func (m *EDSRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EDSRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EDSRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *EDSRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_EDSRequest.Merge(m, src) -} -func (m *EDSRequest) XXX_Size() int { - return m.Size() -} -func (m *EDSRequest) XXX_DiscardUnknown() { - xxx_messageInfo_EDSRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_EDSRequest proto.InternalMessageInfo - -func (m *EDSRequest) GetHash() []byte { - if m != nil { - return m.Hash - } - return nil -} - -type EDSResponse struct { - Status Status `protobuf:"varint,1,opt,name=status,proto3,enum=Status" json:"status,omitempty"` -} - -func (m *EDSResponse) Reset() { *m = EDSResponse{} } -func (m *EDSResponse) String() string { return proto.CompactTextString(m) } -func (*EDSResponse) ProtoMessage() {} -func (*EDSResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_49d42aa96098056e, []int{1} -} -func (m *EDSResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EDSResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EDSResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *EDSResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_EDSResponse.Merge(m, src) -} -func (m *EDSResponse) XXX_Size() int { - return m.Size() -} -func (m *EDSResponse) XXX_DiscardUnknown() { - xxx_messageInfo_EDSResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_EDSResponse proto.InternalMessageInfo - -func (m *EDSResponse) GetStatus() Status { - if m != nil { - return m.Status - } - return Status_INVALID -} - -func init() { - proto.RegisterEnum("Status", Status_name, Status_value) - proto.RegisterType((*EDSRequest)(nil), "EDSRequest") - proto.RegisterType((*EDSResponse)(nil), "EDSResponse") -} - -func init() { - proto.RegisterFile("share/p2p/shrexeds/pb/extended_data_square.proto", fileDescriptor_49d42aa96098056e) -} - -var fileDescriptor_49d42aa96098056e = []byte{ - // 227 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x28, 0xce, 0x48, 0x2c, - 0x4a, 0xd5, 0x2f, 0x30, 0x2a, 0xd0, 0x2f, 0xce, 0x28, 0x4a, 0xad, 0x48, 0x4d, 0x29, 0xd6, 0x2f, - 0x48, 0xd2, 0x4f, 0xad, 0x28, 0x49, 0xcd, 0x4b, 0x49, 0x4d, 0x89, 0x4f, 0x49, 0x2c, 0x49, 0x8c, - 0x2f, 0x2e, 0x2c, 0x4d, 0x2c, 0x4a, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x52, 0xe0, 0xe2, - 0x72, 0x75, 0x09, 0x0e, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x12, 0xe2, 0x62, 0xc9, 0x48, - 0x2c, 0xce, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, 0x02, 0xb3, 0x95, 0xf4, 0xb8, 0xb8, 0xc1, - 0x2a, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0xe4, 0xb9, 0xd8, 0x8a, 0x4b, 0x12, 0x4b, 0x4a, - 0x8b, 0xc1, 0x8a, 0xf8, 0x8c, 0xd8, 0xf5, 0x82, 0xc1, 0xdc, 0x20, 0xa8, 0xb0, 0x96, 0x15, 0x17, - 0x1b, 0x44, 0x44, 0x88, 0x9b, 0x8b, 0xdd, 0xd3, 0x2f, 0xcc, 0xd1, 0xc7, 0xd3, 0x45, 0x80, 0x41, - 0x88, 0x8d, 0x8b, 0xc9, 0xdf, 0x5b, 0x80, 0x51, 0x88, 0x97, 0x8b, 0xd3, 0xcf, 0x3f, 0x24, 0xde, - 0xcd, 0x3f, 0xd4, 0xcf, 0x45, 0x80, 0x49, 0x88, 0x87, 0x8b, 0xc3, 0xd3, 0x2f, 0xc4, 0x35, 0xc8, - 0xcf, 0xd1, 0x47, 0x80, 0xd9, 0x49, 0xe2, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, - 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, - 0x92, 0xd8, 0xc0, 0xce, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x7b, 0x1d, 0xd4, 0xa7, 0xe2, - 0x00, 0x00, 0x00, -} - -func (m *EDSRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *EDSRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *EDSRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Hash) > 0 { - i -= len(m.Hash) - copy(dAtA[i:], m.Hash) - i = encodeVarintExtendedDataSquare(dAtA, i, uint64(len(m.Hash))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *EDSResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *EDSResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *EDSResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Status != 0 { - i = encodeVarintExtendedDataSquare(dAtA, i, uint64(m.Status)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func encodeVarintExtendedDataSquare(dAtA []byte, offset int, v uint64) int { - offset -= sovExtendedDataSquare(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *EDSRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Hash) - if l > 0 { - n += 1 + l + sovExtendedDataSquare(uint64(l)) - } - return n -} - -func (m *EDSResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Status != 0 { - n += 1 + sovExtendedDataSquare(uint64(m.Status)) - } - return n -} - -func sovExtendedDataSquare(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozExtendedDataSquare(x uint64) (n int) { - return sovExtendedDataSquare(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *EDSRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowExtendedDataSquare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EDSRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EDSRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowExtendedDataSquare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthExtendedDataSquare - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthExtendedDataSquare - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) - if m.Hash == nil { - m.Hash = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipExtendedDataSquare(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthExtendedDataSquare - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *EDSResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowExtendedDataSquare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EDSResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EDSResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) - } - m.Status = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowExtendedDataSquare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Status |= Status(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipExtendedDataSquare(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthExtendedDataSquare - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipExtendedDataSquare(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowExtendedDataSquare - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowExtendedDataSquare - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowExtendedDataSquare - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthExtendedDataSquare - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupExtendedDataSquare - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthExtendedDataSquare - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthExtendedDataSquare = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowExtendedDataSquare = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupExtendedDataSquare = fmt.Errorf("proto: unexpected end of group") -) diff --git a/share/p2p/shrexeds/server.go b/share/p2p/shrexeds/server.go deleted file mode 100644 index 15d67d2111..0000000000 --- a/share/p2p/shrexeds/server.go +++ /dev/null @@ -1,202 +0,0 @@ -package shrexeds - -import ( - "context" - "errors" - "fmt" - "io" - "time" - - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/protocol" - "go.uber.org/zap" - - "github.com/celestiaorg/go-libp2p-messenger/serde" - - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/p2p" - p2p_pb "github.com/celestiaorg/celestia-node/share/p2p/shrexeds/pb" -) - -// Server is responsible for serving ODSs for blocksync over the ShrEx/EDS protocol. -type Server struct { - ctx context.Context - cancel context.CancelFunc - - host host.Host - protocolID protocol.ID - - store *eds.Store - - params *Parameters - middleware *p2p.Middleware - metrics *p2p.Metrics -} - -// NewServer creates a new ShrEx/EDS server. -func NewServer(params *Parameters, host host.Host, store *eds.Store) (*Server, error) { - if err := params.Validate(); err != nil { - return nil, fmt.Errorf("shrex-eds: server creation failed: %w", err) - } - - return &Server{ - host: host, - store: store, - protocolID: p2p.ProtocolID(params.NetworkID(), protocolString), - params: params, - middleware: p2p.NewMiddleware(params.ConcurrencyLimit), - }, nil -} - -func (s *Server) Start(context.Context) error { - s.ctx, s.cancel = context.WithCancel(context.Background()) - handler := s.handleStream - withRateLimit := s.middleware.RateLimitHandler(handler) - withRecovery := p2p.RecoveryMiddleware(withRateLimit) - s.host.SetStreamHandler(s.protocolID, withRecovery) - return nil -} - -func (s *Server) Stop(context.Context) error { - defer s.cancel() - s.host.RemoveStreamHandler(s.protocolID) - return nil -} - -func (s *Server) observeRateLimitedRequests() { - numRateLimited := s.middleware.DrainCounter() - if numRateLimited > 0 { - s.metrics.ObserveRequests(context.Background(), numRateLimited, p2p.StatusRateLimited) - } -} - -func (s *Server) handleStream(stream network.Stream) { - logger := log.With("peer", stream.Conn().RemotePeer().String()) - logger.Debug("server: handling eds request") - - s.observeRateLimitedRequests() - - // read request from stream to get the dataHash for store lookup - req, err := s.readRequest(logger, stream) - if err != nil { - logger.Warnw("server: reading request from stream", "err", err) - stream.Reset() //nolint:errcheck - return - } - - // ensure the requested dataHash is a valid root - hash := share.DataHash(req.Hash) - err = hash.Validate() - if err != nil { - logger.Warnw("server: invalid request", "err", err) - stream.Reset() //nolint:errcheck - return - } - logger = logger.With("hash", hash.String()) - - ctx, cancel := context.WithTimeout(s.ctx, s.params.HandleRequestTimeout) - defer cancel() - - // determine whether the EDS is available in our store - // we do not close the reader, so that other requests will not need to re-open the file. - // closing is handled by the LRU cache. - edsReader, err := s.store.GetCAR(ctx, hash) - var status p2p_pb.Status - switch { - case err == nil: - defer func() { - if err := edsReader.Close(); err != nil { - log.Warnw("closing car reader", "err", err) - } - }() - status = p2p_pb.Status_OK - case errors.Is(err, eds.ErrNotFound): - logger.Warnw("server: request hash not found") - s.metrics.ObserveRequests(ctx, 1, p2p.StatusNotFound) - status = p2p_pb.Status_NOT_FOUND - case err != nil: - logger.Errorw("server: get CAR", "err", err) - status = p2p_pb.Status_INTERNAL - } - - // inform the client of our status - err = s.writeStatus(logger, status, stream) - if err != nil { - logger.Warnw("server: writing status to stream", "err", err) - stream.Reset() //nolint:errcheck - return - } - // if we cannot serve the EDS, we are already done - if status != p2p_pb.Status_OK { - err = stream.Close() - if err != nil { - logger.Debugw("server: closing stream", "err", err) - } - return - } - - // start streaming the ODS to the client - err = s.writeODS(logger, edsReader, stream) - if err != nil { - logger.Warnw("server: writing ods to stream", "err", err) - stream.Reset() //nolint:errcheck - return - } - - s.metrics.ObserveRequests(ctx, 1, p2p.StatusSuccess) - err = stream.Close() - if err != nil { - logger.Debugw("server: closing stream", "err", err) - } -} - -func (s *Server) readRequest(logger *zap.SugaredLogger, stream network.Stream) (*p2p_pb.EDSRequest, error) { - err := stream.SetReadDeadline(time.Now().Add(s.params.ServerReadTimeout)) - if err != nil { - logger.Debugw("server: set read deadline", "err", err) - } - - req := new(p2p_pb.EDSRequest) - _, err = serde.Read(stream, req) - if err != nil { - return nil, err - } - err = stream.CloseRead() - if err != nil { - logger.Debugw("server: closing read", "err", err) - } - - return req, nil -} - -func (s *Server) writeStatus(logger *zap.SugaredLogger, status p2p_pb.Status, stream network.Stream) error { - err := stream.SetWriteDeadline(time.Now().Add(s.params.ServerWriteTimeout)) - if err != nil { - logger.Debugw("server: set write deadline", "err", err) - } - - resp := &p2p_pb.EDSResponse{Status: status} - _, err = serde.Write(stream, resp) - return err -} - -func (s *Server) writeODS(logger *zap.SugaredLogger, edsReader io.Reader, stream network.Stream) error { - err := stream.SetWriteDeadline(time.Now().Add(s.params.ServerWriteTimeout)) - if err != nil { - logger.Debugw("server: set read deadline", "err", err) - } - - odsReader, err := eds.ODSReader(edsReader) - if err != nil { - return fmt.Errorf("creating ODS reader: %w", err) - } - buf := make([]byte, s.params.BufferSize) - _, err = io.CopyBuffer(stream, odsReader, buf) - if err != nil { - return fmt.Errorf("writing ODS bytes: %w", err) - } - - return nil -} diff --git a/share/p2p/shrexnd/pb/share.pb.go b/share/p2p/shrexnd/pb/share.pb.go deleted file mode 100644 index 7e3c11416f..0000000000 --- a/share/p2p/shrexnd/pb/share.pb.go +++ /dev/null @@ -1,801 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: share/p2p/shrexnd/pb/share.proto - -package share_p2p_shrex_nd - -import ( - fmt "fmt" - pb "github.com/celestiaorg/nmt/pb" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -type StatusCode int32 - -const ( - StatusCode_INVALID StatusCode = 0 - StatusCode_OK StatusCode = 1 - StatusCode_NOT_FOUND StatusCode = 2 - StatusCode_INTERNAL StatusCode = 3 -) - -var StatusCode_name = map[int32]string{ - 0: "INVALID", - 1: "OK", - 2: "NOT_FOUND", - 3: "INTERNAL", -} - -var StatusCode_value = map[string]int32{ - "INVALID": 0, - "OK": 1, - "NOT_FOUND": 2, - "INTERNAL": 3, -} - -func (x StatusCode) String() string { - return proto.EnumName(StatusCode_name, int32(x)) -} - -func (StatusCode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_ed9f13149b0de397, []int{0} -} - -type GetSharesByNamespaceRequest struct { - RootHash []byte `protobuf:"bytes,1,opt,name=root_hash,json=rootHash,proto3" json:"root_hash,omitempty"` - Namespace []byte `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` -} - -func (m *GetSharesByNamespaceRequest) Reset() { *m = GetSharesByNamespaceRequest{} } -func (m *GetSharesByNamespaceRequest) String() string { return proto.CompactTextString(m) } -func (*GetSharesByNamespaceRequest) ProtoMessage() {} -func (*GetSharesByNamespaceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_ed9f13149b0de397, []int{0} -} -func (m *GetSharesByNamespaceRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetSharesByNamespaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetSharesByNamespaceRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *GetSharesByNamespaceRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetSharesByNamespaceRequest.Merge(m, src) -} -func (m *GetSharesByNamespaceRequest) XXX_Size() int { - return m.Size() -} -func (m *GetSharesByNamespaceRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetSharesByNamespaceRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetSharesByNamespaceRequest proto.InternalMessageInfo - -func (m *GetSharesByNamespaceRequest) GetRootHash() []byte { - if m != nil { - return m.RootHash - } - return nil -} - -func (m *GetSharesByNamespaceRequest) GetNamespace() []byte { - if m != nil { - return m.Namespace - } - return nil -} - -type GetSharesByNamespaceStatusResponse struct { - Status StatusCode `protobuf:"varint,1,opt,name=status,proto3,enum=share.p2p.shrex.nd.StatusCode" json:"status,omitempty"` -} - -func (m *GetSharesByNamespaceStatusResponse) Reset() { *m = GetSharesByNamespaceStatusResponse{} } -func (m *GetSharesByNamespaceStatusResponse) String() string { return proto.CompactTextString(m) } -func (*GetSharesByNamespaceStatusResponse) ProtoMessage() {} -func (*GetSharesByNamespaceStatusResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed9f13149b0de397, []int{1} -} -func (m *GetSharesByNamespaceStatusResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetSharesByNamespaceStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetSharesByNamespaceStatusResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *GetSharesByNamespaceStatusResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetSharesByNamespaceStatusResponse.Merge(m, src) -} -func (m *GetSharesByNamespaceStatusResponse) XXX_Size() int { - return m.Size() -} -func (m *GetSharesByNamespaceStatusResponse) XXX_DiscardUnknown() { - xxx_messageInfo_GetSharesByNamespaceStatusResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_GetSharesByNamespaceStatusResponse proto.InternalMessageInfo - -func (m *GetSharesByNamespaceStatusResponse) GetStatus() StatusCode { - if m != nil { - return m.Status - } - return StatusCode_INVALID -} - -type NamespaceRowResponse struct { - Shares [][]byte `protobuf:"bytes,1,rep,name=shares,proto3" json:"shares,omitempty"` - Proof *pb.Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` -} - -func (m *NamespaceRowResponse) Reset() { *m = NamespaceRowResponse{} } -func (m *NamespaceRowResponse) String() string { return proto.CompactTextString(m) } -func (*NamespaceRowResponse) ProtoMessage() {} -func (*NamespaceRowResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ed9f13149b0de397, []int{2} -} -func (m *NamespaceRowResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NamespaceRowResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NamespaceRowResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *NamespaceRowResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_NamespaceRowResponse.Merge(m, src) -} -func (m *NamespaceRowResponse) XXX_Size() int { - return m.Size() -} -func (m *NamespaceRowResponse) XXX_DiscardUnknown() { - xxx_messageInfo_NamespaceRowResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_NamespaceRowResponse proto.InternalMessageInfo - -func (m *NamespaceRowResponse) GetShares() [][]byte { - if m != nil { - return m.Shares - } - return nil -} - -func (m *NamespaceRowResponse) GetProof() *pb.Proof { - if m != nil { - return m.Proof - } - return nil -} - -func init() { - proto.RegisterEnum("share.p2p.shrex.nd.StatusCode", StatusCode_name, StatusCode_value) - proto.RegisterType((*GetSharesByNamespaceRequest)(nil), "share.p2p.shrex.nd.GetSharesByNamespaceRequest") - proto.RegisterType((*GetSharesByNamespaceStatusResponse)(nil), "share.p2p.shrex.nd.GetSharesByNamespaceStatusResponse") - proto.RegisterType((*NamespaceRowResponse)(nil), "share.p2p.shrex.nd.NamespaceRowResponse") -} - -func init() { proto.RegisterFile("share/p2p/shrexnd/pb/share.proto", fileDescriptor_ed9f13149b0de397) } - -var fileDescriptor_ed9f13149b0de397 = []byte{ - // 326 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x4f, 0x4b, 0xf3, 0x40, - 0x10, 0xc6, 0x93, 0x96, 0x37, 0x6f, 0x3b, 0xad, 0x35, 0x2c, 0x22, 0xc5, 0xca, 0x52, 0x02, 0x42, - 0xf1, 0xb0, 0x81, 0x08, 0x1e, 0x85, 0xd6, 0xfa, 0xa7, 0x58, 0x52, 0xd9, 0xb6, 0xe2, 0x41, 0x28, - 0x1b, 0xbb, 0x92, 0x8b, 0xd9, 0x35, 0xbb, 0x45, 0xfd, 0x16, 0x7e, 0x2c, 0x8f, 0x3d, 0x7a, 0x94, - 0xf6, 0x8b, 0x48, 0xb6, 0xd1, 0x1c, 0xf4, 0xb6, 0xf3, 0xcc, 0x33, 0xbf, 0x7d, 0x66, 0xa0, 0xad, - 0x62, 0x96, 0x72, 0x5f, 0x06, 0xd2, 0x57, 0x71, 0xca, 0x5f, 0x92, 0xb9, 0x2f, 0x23, 0xdf, 0x88, - 0x44, 0xa6, 0x42, 0x0b, 0x84, 0xf2, 0x22, 0x90, 0xc4, 0x38, 0x48, 0x32, 0xdf, 0x6b, 0xc8, 0xc8, - 0x97, 0xa9, 0x10, 0x0f, 0x1b, 0x8f, 0x77, 0x0b, 0xad, 0x0b, 0xae, 0xc7, 0x99, 0x51, 0xf5, 0x5e, - 0x43, 0xf6, 0xc8, 0x95, 0x64, 0xf7, 0x9c, 0xf2, 0xa7, 0x05, 0x57, 0x1a, 0xb5, 0xa0, 0x9a, 0x0a, - 0xa1, 0x67, 0x31, 0x53, 0x71, 0xd3, 0x6e, 0xdb, 0x9d, 0x3a, 0xad, 0x64, 0xc2, 0x25, 0x53, 0x31, - 0xda, 0x87, 0x6a, 0xf2, 0x3d, 0xd0, 0x2c, 0x99, 0x66, 0x21, 0x78, 0x77, 0xe0, 0xfd, 0x45, 0x1e, - 0x6b, 0xa6, 0x17, 0x8a, 0x72, 0x25, 0x45, 0xa2, 0x38, 0x3a, 0x06, 0x47, 0x19, 0xc5, 0xd0, 0x1b, - 0x01, 0x26, 0xbf, 0x43, 0x93, 0xcd, 0xcc, 0xa9, 0x98, 0x73, 0x9a, 0xbb, 0xbd, 0x29, 0xec, 0x14, - 0x61, 0xc5, 0xf3, 0x0f, 0x6f, 0x17, 0x1c, 0x03, 0xc8, 0x78, 0xe5, 0x4e, 0x9d, 0xe6, 0x15, 0x3a, - 0x80, 0x7f, 0x66, 0x6d, 0x93, 0xb3, 0x16, 0x6c, 0x93, 0xfc, 0x08, 0x11, 0xb9, 0xce, 0x1e, 0x74, - 0xd3, 0x3d, 0x3c, 0x01, 0x28, 0x3e, 0x43, 0x35, 0xf8, 0x3f, 0x08, 0x6f, 0xba, 0xc3, 0x41, 0xdf, - 0xb5, 0x90, 0x03, 0xa5, 0xd1, 0x95, 0x6b, 0xa3, 0x2d, 0xa8, 0x86, 0xa3, 0xc9, 0xec, 0x7c, 0x34, - 0x0d, 0xfb, 0x6e, 0x09, 0xd5, 0xa1, 0x32, 0x08, 0x27, 0x67, 0x34, 0xec, 0x0e, 0xdd, 0x72, 0xaf, - 0xf9, 0xbe, 0xc2, 0xf6, 0x72, 0x85, 0xed, 0xcf, 0x15, 0xb6, 0xdf, 0xd6, 0xd8, 0x5a, 0xae, 0xb1, - 0xf5, 0xb1, 0xc6, 0x56, 0xe4, 0x98, 0x7b, 0x1f, 0x7d, 0x05, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x53, - 0xb4, 0x86, 0xb7, 0x01, 0x00, 0x00, -} - -func (m *GetSharesByNamespaceRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetSharesByNamespaceRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *GetSharesByNamespaceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Namespace) > 0 { - i -= len(m.Namespace) - copy(dAtA[i:], m.Namespace) - i = encodeVarintShare(dAtA, i, uint64(len(m.Namespace))) - i-- - dAtA[i] = 0x12 - } - if len(m.RootHash) > 0 { - i -= len(m.RootHash) - copy(dAtA[i:], m.RootHash) - i = encodeVarintShare(dAtA, i, uint64(len(m.RootHash))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *GetSharesByNamespaceStatusResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetSharesByNamespaceStatusResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *GetSharesByNamespaceStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Status != 0 { - i = encodeVarintShare(dAtA, i, uint64(m.Status)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *NamespaceRowResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *NamespaceRowResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *NamespaceRowResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Proof != nil { - { - size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintShare(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Shares) > 0 { - for iNdEx := len(m.Shares) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Shares[iNdEx]) - copy(dAtA[i:], m.Shares[iNdEx]) - i = encodeVarintShare(dAtA, i, uint64(len(m.Shares[iNdEx]))) - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func encodeVarintShare(dAtA []byte, offset int, v uint64) int { - offset -= sovShare(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *GetSharesByNamespaceRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.RootHash) - if l > 0 { - n += 1 + l + sovShare(uint64(l)) - } - l = len(m.Namespace) - if l > 0 { - n += 1 + l + sovShare(uint64(l)) - } - return n -} - -func (m *GetSharesByNamespaceStatusResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Status != 0 { - n += 1 + sovShare(uint64(m.Status)) - } - return n -} - -func (m *NamespaceRowResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Shares) > 0 { - for _, b := range m.Shares { - l = len(b) - n += 1 + l + sovShare(uint64(l)) - } - } - if m.Proof != nil { - l = m.Proof.Size() - n += 1 + l + sovShare(uint64(l)) - } - return n -} - -func sovShare(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozShare(x uint64) (n int) { - return sovShare(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *GetSharesByNamespaceRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetSharesByNamespaceRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetSharesByNamespaceRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RootHash", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthShare - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthShare - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RootHash = append(m.RootHash[:0], dAtA[iNdEx:postIndex]...) - if m.RootHash == nil { - m.RootHash = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthShare - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthShare - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Namespace = append(m.Namespace[:0], dAtA[iNdEx:postIndex]...) - if m.Namespace == nil { - m.Namespace = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipShare(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthShare - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetSharesByNamespaceStatusResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetSharesByNamespaceStatusResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetSharesByNamespaceStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) - } - m.Status = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Status |= StatusCode(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipShare(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthShare - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NamespaceRowResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: NamespaceRowResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NamespaceRowResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthShare - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthShare - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Shares = append(m.Shares, make([]byte, postIndex-iNdEx)) - copy(m.Shares[len(m.Shares)-1], dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowShare - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthShare - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthShare - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Proof == nil { - m.Proof = &pb.Proof{} - } - if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipShare(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthShare - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipShare(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowShare - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowShare - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowShare - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthShare - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupShare - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthShare - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthShare = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowShare = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupShare = fmt.Errorf("proto: unexpected end of group") -) diff --git a/share/p2p/shrexnd/pb/share.proto b/share/p2p/shrexnd/pb/share.proto deleted file mode 100644 index a5bdbfa071..0000000000 --- a/share/p2p/shrexnd/pb/share.proto +++ /dev/null @@ -1,25 +0,0 @@ -syntax = "proto3"; - -package share.p2p.shrex.nd; -import "pb/proof.proto"; - -message GetSharesByNamespaceRequest{ - bytes root_hash = 1; - bytes namespace = 2; -} - -message GetSharesByNamespaceStatusResponse{ - StatusCode status = 1; -} - -enum StatusCode { - INVALID = 0; - OK = 1; - NOT_FOUND = 2; - INTERNAL = 3; -}; - -message NamespaceRowResponse { - repeated bytes shares = 1; - proof.pb.Proof proof = 2; -} diff --git a/share/p2p/shrexnd/server.go b/share/p2p/shrexnd/server.go deleted file mode 100644 index 9773ad7327..0000000000 --- a/share/p2p/shrexnd/server.go +++ /dev/null @@ -1,257 +0,0 @@ -package shrexnd - -import ( - "context" - "crypto/sha256" - "errors" - "fmt" - "time" - - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/protocol" - "go.uber.org/zap" - - "github.com/celestiaorg/go-libp2p-messenger/serde" - nmt_pb "github.com/celestiaorg/nmt/pb" - - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/p2p" - pb "github.com/celestiaorg/celestia-node/share/p2p/shrexnd/pb" -) - -// Server implements server side of shrex/nd protocol to serve namespaced share to remote -// peers. -type Server struct { - cancel context.CancelFunc - - host host.Host - protocolID protocol.ID - - handler network.StreamHandler - store *eds.Store - - params *Parameters - middleware *p2p.Middleware - metrics *p2p.Metrics -} - -// NewServer creates new Server -func NewServer(params *Parameters, host host.Host, store *eds.Store) (*Server, error) { - if err := params.Validate(); err != nil { - return nil, fmt.Errorf("shrex-nd: server creation failed: %w", err) - } - - srv := &Server{ - store: store, - host: host, - params: params, - protocolID: p2p.ProtocolID(params.NetworkID(), protocolString), - middleware: p2p.NewMiddleware(params.ConcurrencyLimit), - } - - ctx, cancel := context.WithCancel(context.Background()) - srv.cancel = cancel - - handler := srv.streamHandler(ctx) - withRateLimit := srv.middleware.RateLimitHandler(handler) - withRecovery := p2p.RecoveryMiddleware(withRateLimit) - srv.handler = withRecovery - return srv, nil -} - -// Start starts the server -func (srv *Server) Start(context.Context) error { - srv.host.SetStreamHandler(srv.protocolID, srv.handler) - return nil -} - -// Stop stops the server -func (srv *Server) Stop(context.Context) error { - srv.cancel() - srv.host.RemoveStreamHandler(srv.protocolID) - return nil -} - -func (srv *Server) streamHandler(ctx context.Context) network.StreamHandler { - return func(s network.Stream) { - err := srv.handleNamespacedData(ctx, s) - if err != nil { - s.Reset() //nolint:errcheck - return - } - if err = s.Close(); err != nil { - log.Debugw("server: closing stream", "err", err) - } - } -} - -// SetHandler sets server handler -func (srv *Server) SetHandler(handler network.StreamHandler) { - srv.handler = handler -} - -func (srv *Server) observeRateLimitedRequests() { - numRateLimited := srv.middleware.DrainCounter() - if numRateLimited > 0 { - srv.metrics.ObserveRequests(context.Background(), numRateLimited, p2p.StatusRateLimited) - } -} - -func (srv *Server) handleNamespacedData(ctx context.Context, stream network.Stream) error { - logger := log.With("source", "server", "peer", stream.Conn().RemotePeer().String()) - logger.Debug("handling nd request") - - srv.observeRateLimitedRequests() - req, err := srv.readRequest(logger, stream) - if err != nil { - logger.Warnw("read request", "err", err) - srv.metrics.ObserveRequests(ctx, 1, p2p.StatusBadRequest) - return err - } - - logger = logger.With("namespace", share.Namespace(req.Namespace).String(), - "hash", share.DataHash(req.RootHash).String()) - - ctx, cancel := context.WithTimeout(ctx, srv.params.HandleRequestTimeout) - defer cancel() - - shares, status, err := srv.getNamespaceData(ctx, req.RootHash, req.Namespace) - if err != nil { - // server should respond with status regardless if there was an error getting data - sendErr := srv.respondStatus(ctx, logger, stream, status) - if sendErr != nil { - logger.Errorw("sending response", "err", sendErr) - srv.metrics.ObserveRequests(ctx, 1, p2p.StatusSendRespErr) - } - logger.Errorw("handling request", "err", err) - return errors.Join(err, sendErr) - } - - err = srv.respondStatus(ctx, logger, stream, status) - if err != nil { - logger.Errorw("sending response", "err", err) - srv.metrics.ObserveRequests(ctx, 1, p2p.StatusSendRespErr) - return err - } - - err = srv.sendNamespacedShares(shares, stream) - if err != nil { - logger.Errorw("send nd data", "err", err) - srv.metrics.ObserveRequests(ctx, 1, p2p.StatusSendRespErr) - return err - } - return nil -} - -func (srv *Server) readRequest( - logger *zap.SugaredLogger, - stream network.Stream, -) (*pb.GetSharesByNamespaceRequest, error) { - err := stream.SetReadDeadline(time.Now().Add(srv.params.ServerReadTimeout)) - if err != nil { - logger.Debugw("setting read deadline", "err", err) - } - - var req pb.GetSharesByNamespaceRequest - _, err = serde.Read(stream, &req) - if err != nil { - return nil, fmt.Errorf("reading request: %w", err) - } - - logger.Debugw("new request") - err = stream.CloseRead() - if err != nil { - logger.Debugw("closing read side of the stream", "err", err) - } - - err = validateRequest(req) - if err != nil { - return nil, fmt.Errorf("invalid request: %w", err) - } - return &req, nil -} - -func (srv *Server) getNamespaceData(ctx context.Context, - hash share.DataHash, namespace share.Namespace, -) (share.NamespacedShares, pb.StatusCode, error) { - dah, err := srv.store.GetDAH(ctx, hash) - if err != nil { - if errors.Is(err, eds.ErrNotFound) { - return nil, pb.StatusCode_NOT_FOUND, nil - } - return nil, pb.StatusCode_INTERNAL, fmt.Errorf("retrieving DAH: %w", err) - } - - shares, err := eds.RetrieveNamespaceFromStore(ctx, srv.store, dah, namespace) - if err != nil { - return nil, pb.StatusCode_INTERNAL, fmt.Errorf("retrieving shares: %w", err) - } - - return shares, pb.StatusCode_OK, nil -} - -func (srv *Server) respondStatus( - ctx context.Context, - logger *zap.SugaredLogger, - stream network.Stream, - status pb.StatusCode, -) error { - srv.observeStatus(ctx, status) - - err := stream.SetWriteDeadline(time.Now().Add(srv.params.ServerWriteTimeout)) - if err != nil { - logger.Debugw("setting write deadline", "err", err) - } - - _, err = serde.Write(stream, &pb.GetSharesByNamespaceStatusResponse{Status: status}) - if err != nil { - return fmt.Errorf("writing response: %w", err) - } - - return nil -} - -// sendNamespacedShares encodes shares into proto messages and sends it to client -func (srv *Server) sendNamespacedShares(shares share.NamespacedShares, stream network.Stream) error { - for _, row := range shares { - row := &pb.NamespaceRowResponse{ - Shares: row.Shares, - Proof: &nmt_pb.Proof{ - Start: int64(row.Proof.Start()), - End: int64(row.Proof.End()), - Nodes: row.Proof.Nodes(), - LeafHash: row.Proof.LeafHash(), - IsMaxNamespaceIgnored: row.Proof.IsMaxNamespaceIDIgnored(), - }, - } - _, err := serde.Write(stream, row) - if err != nil { - return fmt.Errorf("writing nd data to stream: %w", err) - } - } - return nil -} - -func (srv *Server) observeStatus(ctx context.Context, status pb.StatusCode) { - switch { - case status == pb.StatusCode_OK: - srv.metrics.ObserveRequests(ctx, 1, p2p.StatusSuccess) - case status == pb.StatusCode_NOT_FOUND: - srv.metrics.ObserveRequests(ctx, 1, p2p.StatusNotFound) - case status == pb.StatusCode_INTERNAL: - srv.metrics.ObserveRequests(ctx, 1, p2p.StatusInternalErr) - } -} - -// validateRequest checks correctness of the request -func validateRequest(req pb.GetSharesByNamespaceRequest) error { - if err := share.Namespace(req.Namespace).ValidateForData(); err != nil { - return err - } - if len(req.RootHash) != sha256.Size { - return fmt.Errorf("incorrect root hash length: %v", len(req.RootHash)) - } - return nil -} diff --git a/share/root.go b/share/root.go new file mode 100644 index 0000000000..e763c044b2 --- /dev/null +++ b/share/root.go @@ -0,0 +1,123 @@ +package share + +import ( + "bytes" + "crypto/sha256" + "fmt" + "hash" + + "github.com/celestiaorg/celestia-app/v3/pkg/da" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" +) + +const ( + // DataHashSize is the size of the DataHash. + DataHashSize = 32 + // AxisRootSize is the size of the single root in AxisRoots. + AxisRootSize = 90 +) + +// AxisRoots represents root commitment to multiple Shares. +// In practice, it is a commitment to all the Data in a square. +type AxisRoots = da.DataAvailabilityHeader + +// DataHash is a representation of the AxisRoots hash. +type DataHash []byte + +func (dh DataHash) Validate() error { + if len(dh) != DataHashSize { + return fmt.Errorf("invalid hash size, expected 32, got %d", len(dh)) + } + return nil +} + +func (dh DataHash) String() string { + return fmt.Sprintf("%X", []byte(dh)) +} + +// IsEmptyEDS check whether DataHash corresponds to the root of an empty block EDS. +func (dh DataHash) IsEmptyEDS() bool { + return bytes.Equal(EmptyEDSDataHash(), dh) +} + +// NewSHA256Hasher returns a new instance of a SHA-256 hasher. +func NewSHA256Hasher() hash.Hash { + return sha256.New() +} + +// NewAxisRoots generates AxisRoots(DataAvailabilityHeader) using the +// provided extended data square. +func NewAxisRoots(eds *rsmt2d.ExtendedDataSquare) (*AxisRoots, error) { + dah, err := da.NewDataAvailabilityHeader(eds) + if err != nil { + return nil, err + } + return &dah, nil +} + +// RowsWithNamespace inspects the AxisRoots for the Namespace and provides +// a slices of Row indexes containing the namespace. +func RowsWithNamespace(root *AxisRoots, namespace libshare.Namespace) (idxs []int, err error) { + for i, row := range root.RowRoots { + outside, err := IsOutsideRange(namespace, row, row) + if err != nil { + return nil, err + } + if !outside { + idxs = append(idxs, i) + } + } + return +} + +// RootHashForCoordinates returns the root hash for the given coordinates. +func RootHashForCoordinates(r *AxisRoots, axisType rsmt2d.Axis, rowIdx, colIdx uint) []byte { + if axisType == rsmt2d.Row { + return r.RowRoots[rowIdx] + } + return r.ColumnRoots[colIdx] +} + +// IsOutsideRange checks if the namespace is outside the min-max range of the given hashes. +func IsOutsideRange(namespace libshare.Namespace, leftHash, rightHash []byte) (bool, error) { + if len(leftHash) < libshare.NamespaceSize { + return false, fmt.Errorf("left can't be less than %d", libshare.NamespaceSize) + } + if len(rightHash) < 2*libshare.NamespaceSize { + return false, fmt.Errorf("rightHash can't be less than %d", 2*libshare.NamespaceSize) + } + ns1, err := libshare.NewNamespaceFromBytes(leftHash[:libshare.NamespaceSize]) + if err != nil { + return false, err + } + ns2, err := libshare.NewNamespaceFromBytes(rightHash[libshare.NamespaceSize : libshare.NamespaceSize*2]) + if err != nil { + return false, err + } + return namespace.IsLessThan(ns1) || !namespace.IsLessOrEqualThan(ns2), nil +} + +// IsAboveMax checks if the namespace is above the maximum namespace of the given hash. +func IsAboveMax(namespace libshare.Namespace, hash []byte) (bool, error) { + if len(hash) < 2*libshare.NamespaceSize { + return false, fmt.Errorf("hash can't be less than: %d", 2*libshare.NamespaceSize) + } + ns, err := libshare.NewNamespaceFromBytes(hash[libshare.NamespaceSize : libshare.NamespaceSize*2]) + if err != nil { + return false, err + } + return !namespace.IsLessOrEqualThan(ns), nil +} + +// IsBelowMin checks if the target namespace is below the minimum namespace of the given hash. +func IsBelowMin(namespace libshare.Namespace, hash []byte) (bool, error) { + if len(hash) < libshare.NamespaceSize { + return false, fmt.Errorf("hash can't be less than: %d", libshare.NamespaceSize) + } + ns1, err := libshare.NewNamespaceFromBytes(hash[:libshare.NamespaceSize]) + if err != nil { + return false, err + } + return namespace.IsLessThan(ns1), nil +} diff --git a/share/share.go b/share/share.go index 6ad4bbf40f..7bfcd40a99 100644 --- a/share/share.go +++ b/share/share.go @@ -1,76 +1,12 @@ package share import ( - "bytes" - "crypto/sha256" - "encoding/hex" - "fmt" - "hash" - - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" ) // DefaultRSMT2DCodec sets the default rsmt2d.Codec for shares. var DefaultRSMT2DCodec = appconsts.DefaultCodec -const ( - // Size is a system-wide size of a share, including both data and namespace GetNamespace - Size = appconsts.ShareSize -) - // MaxSquareSize is currently the maximum size supported for unerasured data in // rsmt2d.ExtendedDataSquare. var MaxSquareSize = appconsts.SquareSizeUpperBound(appconsts.LatestVersion) - -// Share contains the raw share data without the corresponding namespace. -// NOTE: Alias for the byte is chosen to keep maximal compatibility, especially with rsmt2d. -// Ideally, we should define reusable type elsewhere and make everyone(Core, rsmt2d, ipld) to rely -// on it. -type Share = []byte - -// GetNamespace slices Namespace out of the Share. -func GetNamespace(s Share) Namespace { - return s[:NamespaceSize] -} - -// GetData slices out data of the Share. -func GetData(s Share) []byte { - return s[NamespaceSize:] -} - -// DataHash is a representation of the Root hash. -type DataHash []byte - -func (dh DataHash) Validate() error { - if len(dh) != 32 { - return fmt.Errorf("invalid hash size, expected 32, got %d", len(dh)) - } - return nil -} - -func (dh DataHash) String() string { - return fmt.Sprintf("%X", []byte(dh)) -} - -// IsEmptyRoot check whether DataHash corresponds to the root of an empty block EDS. -func (dh DataHash) IsEmptyRoot() bool { - return bytes.Equal(EmptyRoot().Hash(), dh) -} - -// MustDataHashFromString converts a hex string to a valid datahash. -func MustDataHashFromString(datahash string) DataHash { - dh, err := hex.DecodeString(datahash) - if err != nil { - panic(fmt.Sprintf("datahash conversion: passed string was not valid hex: %s", datahash)) - } - err = DataHash(dh).Validate() - if err != nil { - panic(fmt.Sprintf("datahash validation: passed hex string failed: %s", err)) - } - return dh -} - -// NewSHA256Hasher returns a new instance of a SHA-256 hasher. -func NewSHA256Hasher() hash.Hash { - return sha256.New() -} diff --git a/share/sharetest/testing.go b/share/sharetest/testing.go deleted file mode 100644 index 8d9b824b69..0000000000 --- a/share/sharetest/testing.go +++ /dev/null @@ -1,78 +0,0 @@ -package sharetest - -import ( - "bytes" - "math/rand" - "sort" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/go-square/namespace" - - "github.com/celestiaorg/celestia-node/share" -) - -// RandShares generate 'total' amount of shares filled with random data. -func RandShares(t testing.TB, total int) []share.Share { - if total&(total-1) != 0 { - t.Errorf("total must be power of 2: %d", total) - t.FailNow() - } - - shares := make([]share.Share, total) - for i := range shares { - shr := make([]byte, share.Size) - copy(share.GetNamespace(shr), RandV0Namespace()) - rndMu.Lock() - _, err := rnd.Read(share.GetData(shr)) - rndMu.Unlock() - require.NoError(t, err) - shares[i] = shr - } - sort.Slice(shares, func(i, j int) bool { return bytes.Compare(shares[i], shares[j]) < 0 }) - - return shares -} - -// RandSharesWithNamespace is same the as RandShares, but sets same namespace for all shares. -func RandSharesWithNamespace(t testing.TB, namespace share.Namespace, total int) []share.Share { - if total&(total-1) != 0 { - t.Errorf("total must be power of 2: %d", total) - t.FailNow() - } - - shares := make([]share.Share, total) - rnd := rand.New(rand.NewSource(time.Now().Unix())) //nolint:gosec - for i := range shares { - shr := make([]byte, share.Size) - copy(share.GetNamespace(shr), namespace) - _, err := rnd.Read(share.GetData(shr)) - require.NoError(t, err) - shares[i] = shr - } - sort.Slice(shares, func(i, j int) bool { return bytes.Compare(shares[i], shares[j]) < 0 }) - return shares -} - -// RandV0Namespace generates random valid data namespace for testing purposes. -func RandV0Namespace() share.Namespace { - rb := make([]byte, namespace.NamespaceVersionZeroIDSize) - rndMu.Lock() - rnd.Read(rb) - rndMu.Unlock() - for { - namespace, _ := share.NewBlobNamespaceV0(rb) - if err := namespace.ValidateForData(); err != nil { - continue - } - return namespace - } -} - -var ( - rnd = rand.New(rand.NewSource(time.Now().Unix())) //nolint:gosec - rndMu sync.Mutex -) diff --git a/share/shwap/eds.go b/share/shwap/eds.go new file mode 100644 index 0000000000..2d856c5317 --- /dev/null +++ b/share/shwap/eds.go @@ -0,0 +1,6 @@ +package shwap + +// EDSName is the name identifier for the Extended Data Square. +const EDSName = "eds_v0" + +// NOTE: There is no EDS container as it's already defined by rsmt2d and eds.Accessor interface. diff --git a/share/shwap/eds_id.go b/share/shwap/eds_id.go new file mode 100644 index 0000000000..75805f11fb --- /dev/null +++ b/share/shwap/eds_id.go @@ -0,0 +1,95 @@ +package shwap + +import ( + "encoding/binary" + "fmt" + "io" +) + +// EdsIDSize defines the byte size of the EdsID. +const EdsIDSize = 8 + +// EdsID represents a unique identifier for a row, using the height of the block +// to identify the data square in the chain. +type EdsID struct { + Height uint64 // Height specifies the block height. +} + +// NewEdsID creates a new EdsID using the given height. +func NewEdsID(height uint64) (EdsID, error) { + eid := EdsID{ + Height: height, + } + return eid, eid.Validate() +} + +// EdsIDFromBinary decodes a byte slice into an EdsID, validating the length of the data. +// It returns an error if the data slice does not match the expected size of an EdsID. +func EdsIDFromBinary(data []byte) (EdsID, error) { + if len(data) != EdsIDSize { + return EdsID{}, fmt.Errorf("invalid EdsID data length: %d != %d", len(data), EdsIDSize) + } + eid := EdsID{ + Height: binary.BigEndian.Uint64(data), + } + if err := eid.Validate(); err != nil { + return EdsID{}, fmt.Errorf("validating EdsID: %w", err) + } + + return eid, nil +} + +// Equals checks equality of EdsIDs. +func (eid *EdsID) Equals(other EdsID) bool { + return eid.Height == other.Height +} + +// ReadFrom reads the binary form of EdsID from the provided reader. +func (eid *EdsID) ReadFrom(r io.Reader) (int64, error) { + data := make([]byte, EdsIDSize) + n, err := io.ReadFull(r, data) + if err != nil { + return int64(n), err + } + if n != EdsIDSize { + return int64(n), fmt.Errorf("EdsID: expected %d bytes, got %d", EdsIDSize, n) + } + id, err := EdsIDFromBinary(data) + if err != nil { + return int64(n), fmt.Errorf("EdsIDFromBinary: %w", err) + } + *eid = id + return int64(n), nil +} + +// MarshalBinary encodes an EdsID into its binary form, primarily for storage or network +// transmission. +func (eid EdsID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, EdsIDSize) + return eid.appendTo(data), nil +} + +// WriteTo writes the binary form of EdsID to the provided writer. +func (eid EdsID) WriteTo(w io.Writer) (int64, error) { + data, err := eid.MarshalBinary() + if err != nil { + return 0, err + } + n, err := w.Write(data) + return int64(n), err +} + +// Validate checks the integrity of an EdsID's fields against the provided Root. +// It ensures that the EdsID is not constructed with a zero Height and that the root is not nil. +func (eid EdsID) Validate() error { + if eid.Height == 0 { + return fmt.Errorf("%w: Height == 0", ErrInvalidID) + } + return nil +} + +// appendTo helps in the binary encoding of EdsID by appending the binary form of Height to the +// given byte slice. +func (eid EdsID) appendTo(data []byte) []byte { + return binary.BigEndian.AppendUint64(data, eid.Height) +} diff --git a/share/shwap/eds_id_test.go b/share/shwap/eds_id_test.go new file mode 100644 index 0000000000..be6cd21537 --- /dev/null +++ b/share/shwap/eds_id_test.go @@ -0,0 +1,42 @@ +package shwap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEdsID(t *testing.T) { + id, err := NewEdsID(2) + require.NoError(t, err) + + data, err := id.MarshalBinary() + require.NoError(t, err) + + idOut, err := EdsIDFromBinary(data) + require.NoError(t, err) + assert.EqualValues(t, id, idOut) + + err = idOut.Validate() + require.NoError(t, err) + require.True(t, id.Equals(idOut)) +} + +func TestEdsIDReaderWriter(t *testing.T) { + id, err := NewEdsID(2) + require.NoError(t, err) + + buf := bytes.NewBuffer(nil) + n, err := id.WriteTo(buf) + require.NoError(t, err) + require.Equal(t, int64(EdsIDSize), n) + + eidOut := EdsID{} + n, err = eidOut.ReadFrom(buf) + require.NoError(t, err) + require.Equal(t, int64(EdsIDSize), n) + + require.EqualValues(t, id, eidOut) +} diff --git a/share/shwap/getter.go b/share/shwap/getter.go new file mode 100644 index 0000000000..f56c8edc63 --- /dev/null +++ b/share/shwap/getter.go @@ -0,0 +1,50 @@ +package shwap + +import ( + "context" + "errors" + "fmt" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/header" +) + +var ( + // ErrOperationNotSupported is used to indicate that the operation is not supported by the + // implementation of the getter interface. + ErrOperationNotSupported = errors.New("operation is not supported") + // ErrNotFound is used to indicate that requested data could not be found. + ErrNotFound = errors.New("data not found") + // ErrInvalidID is used to indicate that an ID failed validation. + ErrInvalidID = errors.New("invalid shwap ID") + // ErrOutOfBounds is used to indicate that a passed row or column index is out of bounds of the + // square size. + ErrOutOfBounds = fmt.Errorf("index out of bounds: %w", ErrInvalidID) + // ErrNoSampleIndicies is used to indicate that no indicies where given to process. + ErrNoSampleIndicies = errors.New("no sample indicies to fetch") +) + +// Getter interface provides a set of accessors for shares by the Root. +// Automatically verifies integrity of shares(exceptions possible depending on the implementation). +// +//go:generate mockgen -destination=getters/mock/getter.go -package=mock . Getter +type Getter interface { + // GetSamples gets samples by their indices. + // Returns Sample slice with requested number of samples in the requested order. + // May return partial response with some samples being empty if they weren't found. + GetSamples(ctx context.Context, header *header.ExtendedHeader, indices []SampleCoords) ([]Sample, error) + + // GetEDS gets the full EDS identified by the given extended header. + GetEDS(context.Context, *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) + + // GetRow gets Row by its index committed to the given extended header. + GetRow(ctx context.Context, header *header.ExtendedHeader, rowIdx int) (Row, error) + // GetNamespaceData gets all shares from an EDS within the given namespace. + // Shares are returned in a row-by-row order if the namespace spans multiple rows. + // Inclusion of returned data could be verified using Verify method on NamespacedShares. + // If no shares are found for target namespace non-inclusion could be also verified by calling + // Verify method. + GetNamespaceData(context.Context, *header.ExtendedHeader, libshare.Namespace) (NamespaceData, error) +} diff --git a/share/getters/cascade.go b/share/shwap/getters/cascade.go similarity index 59% rename from share/getters/cascade.go rename to share/shwap/getters/cascade.go index 3b8e8060bd..c9ac091ddd 100644 --- a/share/getters/cascade.go +++ b/share/shwap/getters/cascade.go @@ -5,85 +5,98 @@ import ( "errors" "time" + logging "github.com/ipfs/go-log/v2" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds/byzantine" + "github.com/celestiaorg/celestia-node/share/shwap" ) -var _ share.Getter = (*CascadeGetter)(nil) +var ( + tracer = otel.Tracer("share/getters") + log = logging.Logger("share/getters") +) + +var _ shwap.Getter = (*CascadeGetter)(nil) -// CascadeGetter implements custom share.Getter that composes multiple Getter implementations in +// CascadeGetter implements custom shwap.Getter that composes multiple Getter implementations in // "cascading" order. // // See cascade func for details on cascading. type CascadeGetter struct { - getters []share.Getter + getters []shwap.Getter } -// NewCascadeGetter instantiates a new CascadeGetter from given share.Getters with given interval. -func NewCascadeGetter(getters []share.Getter) *CascadeGetter { +// NewCascadeGetter instantiates a new CascadeGetter from given shwap.Getters with given interval. +func NewCascadeGetter(getters []shwap.Getter) *CascadeGetter { return &CascadeGetter{ getters: getters, } } -// GetShare gets a share from any of registered share.Getters in cascading order. -func (cg *CascadeGetter) GetShare( - ctx context.Context, header *header.ExtendedHeader, row, col int, -) (share.Share, error) { - ctx, span := tracer.Start(ctx, "cascade/get-share", trace.WithAttributes( - attribute.Int("row", row), - attribute.Int("col", col), +// GetSamples gets samples from any of registered shwap.Getters in cascading order. +func (cg *CascadeGetter) GetSamples(ctx context.Context, hdr *header.ExtendedHeader, + indices []shwap.SampleCoords, +) ([]shwap.Sample, error) { + ctx, span := tracer.Start(ctx, "cascade/get-samples", trace.WithAttributes( + attribute.Int("amount", len(indices)), )) defer span.End() - upperBound := len(header.DAH.RowRoots) - if row >= upperBound || col >= upperBound { - err := share.ErrOutOfBounds - span.RecordError(err) - return nil, err - } - get := func(ctx context.Context, get share.Getter) (share.Share, error) { - return get.GetShare(ctx, header, row, col) + get := func(ctx context.Context, get shwap.Getter) ([]shwap.Sample, error) { + return get.GetSamples(ctx, hdr, indices) } return cascadeGetters(ctx, cg.getters, get) } -// GetEDS gets a full EDS from any of registered share.Getters in cascading order. +// GetEDS gets a full EDS from any of registered shwap.Getters in cascading order. func (cg *CascadeGetter) GetEDS( ctx context.Context, header *header.ExtendedHeader, ) (*rsmt2d.ExtendedDataSquare, error) { ctx, span := tracer.Start(ctx, "cascade/get-eds") defer span.End() - get := func(ctx context.Context, get share.Getter) (*rsmt2d.ExtendedDataSquare, error) { + get := func(ctx context.Context, get shwap.Getter) (*rsmt2d.ExtendedDataSquare, error) { return get.GetEDS(ctx, header) } return cascadeGetters(ctx, cg.getters, get) } -// GetSharesByNamespace gets NamespacedShares from any of registered share.Getters in cascading +// GetRow gets row shares from any of registered shwap.Getters in cascading +// order. +func (cg *CascadeGetter) GetRow(ctx context.Context, header *header.ExtendedHeader, rowIdx int) (shwap.Row, error) { + ctx, span := tracer.Start(ctx, "cascade/get-row") + defer span.End() + + get := func(ctx context.Context, get shwap.Getter) (shwap.Row, error) { + return get.GetRow(ctx, header, rowIdx) + } + return cascadeGetters(ctx, cg.getters, get) +} + +// GetNamespaceData gets NamespacedShares from any of registered shwap.Getters in cascading // order. -func (cg *CascadeGetter) GetSharesByNamespace( +func (cg *CascadeGetter) GetNamespaceData( ctx context.Context, header *header.ExtendedHeader, - namespace share.Namespace, -) (share.NamespacedShares, error) { + namespace libshare.Namespace, +) (shwap.NamespaceData, error) { ctx, span := tracer.Start(ctx, "cascade/get-shares-by-namespace", trace.WithAttributes( attribute.String("namespace", namespace.String()), )) defer span.End() - get := func(ctx context.Context, get share.Getter) (share.NamespacedShares, error) { - return get.GetSharesByNamespace(ctx, header, namespace) + get := func(ctx context.Context, get shwap.Getter) (shwap.NamespaceData, error) { + return get.GetNamespaceData(ctx, header, namespace) } return cascadeGetters(ctx, cg.getters, get) @@ -99,8 +112,8 @@ func (cg *CascadeGetter) GetSharesByNamespace( // NOTE: New source attempts after interval do suspend running sources in progress. func cascadeGetters[V any]( ctx context.Context, - getters []share.Getter, - get func(context.Context, share.Getter) (V, error), + getters []shwap.Getter, + get func(context.Context, shwap.Getter) (V, error), ) (V, error) { var ( zero V @@ -134,14 +147,14 @@ func cascadeGetters[V any]( // we split the timeout between left getters // once async cascadegetter is implemented, we can remove this - getCtx, cancel := ctxWithSplitTimeout(ctx, len(getters)-i, minTimeout) + getCtx, cancel := utils.CtxWithSplitTimeout(ctx, len(getters)-i, minTimeout) val, getErr := get(getCtx, getter) cancel() if getErr == nil { return val, nil } - if errors.Is(getErr, errOperationNotSupported) { + if errors.Is(getErr, shwap.ErrOperationNotSupported) { continue } diff --git a/share/getters/cascade_test.go b/share/shwap/getters/cascade_test.go similarity index 76% rename from share/getters/cascade_test.go rename to share/shwap/getters/cascade_test.go index fc2f663bdc..8237d8ca5c 100644 --- a/share/getters/cascade_test.go +++ b/share/shwap/getters/cascade_test.go @@ -12,8 +12,8 @@ import ( "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/mocks" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/getters/mock" ) func TestCascadeGetter(t *testing.T) { @@ -22,7 +22,7 @@ func TestCascadeGetter(t *testing.T) { const gettersN = 3 headers := make([]*header.ExtendedHeader, gettersN) - getters := make([]share.Getter, gettersN) + getters := make([]shwap.Getter, gettersN) for i := range headers { getters[i], headers[i] = TestGetter(t) } @@ -30,7 +30,7 @@ func TestCascadeGetter(t *testing.T) { getter := NewCascadeGetter(getters) t.Run("GetShare", func(t *testing.T) { for _, eh := range headers { - sh, err := getter.GetShare(ctx, eh, 0, 0) + sh, err := getter.GetSamples(ctx, eh, []shwap.SampleCoords{{}}) assert.NoError(t, err) assert.NotEmpty(t, sh) } @@ -50,10 +50,10 @@ func TestCascade(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - timeoutGetter := mocks.NewMockGetter(ctrl) - immediateFailGetter := mocks.NewMockGetter(ctrl) - successGetter := mocks.NewMockGetter(ctrl) - ctxGetter := mocks.NewMockGetter(ctrl) + timeoutGetter := mock.NewMockGetter(ctrl) + immediateFailGetter := mock.NewMockGetter(ctrl) + successGetter := mock.NewMockGetter(ctrl) + ctxGetter := mock.NewMockGetter(ctrl) timeoutGetter.EXPECT().GetEDS(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, _ *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { return nil, context.DeadlineExceeded @@ -67,43 +67,43 @@ func TestCascade(t *testing.T) { return nil, ctx.Err() }).AnyTimes() - stuckGetter := mocks.NewMockGetter(ctrl) + stuckGetter := mock.NewMockGetter(ctrl) stuckGetter.EXPECT().GetEDS(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, _ *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { <-ctx.Done() return nil, ctx.Err() }).AnyTimes() - get := func(ctx context.Context, get share.Getter) (*rsmt2d.ExtendedDataSquare, error) { + get := func(ctx context.Context, get shwap.Getter) (*rsmt2d.ExtendedDataSquare, error) { return get.GetEDS(ctx, nil) } t.Run("SuccessFirst", func(t *testing.T) { - getters := []share.Getter{successGetter, timeoutGetter, immediateFailGetter} + getters := []shwap.Getter{successGetter, timeoutGetter, immediateFailGetter} _, err := cascadeGetters(ctx, getters, get) assert.NoError(t, err) }) t.Run("SuccessSecond", func(t *testing.T) { - getters := []share.Getter{immediateFailGetter, successGetter} + getters := []shwap.Getter{immediateFailGetter, successGetter} _, err := cascadeGetters(ctx, getters, get) assert.NoError(t, err) }) t.Run("SuccessSecondAfterFirst", func(t *testing.T) { - getters := []share.Getter{timeoutGetter, successGetter} + getters := []shwap.Getter{timeoutGetter, successGetter} _, err := cascadeGetters(ctx, getters, get) assert.NoError(t, err) }) t.Run("SuccessAfterMultipleTimeouts", func(t *testing.T) { - getters := []share.Getter{timeoutGetter, immediateFailGetter, timeoutGetter, timeoutGetter, successGetter} + getters := []shwap.Getter{timeoutGetter, immediateFailGetter, timeoutGetter, timeoutGetter, successGetter} _, err := cascadeGetters(ctx, getters, get) assert.NoError(t, err) }) t.Run("Error", func(t *testing.T) { - getters := []share.Getter{immediateFailGetter, timeoutGetter, immediateFailGetter} + getters := []shwap.Getter{immediateFailGetter, timeoutGetter, immediateFailGetter} _, err := cascadeGetters(ctx, getters, get) assert.Error(t, err) assert.Equal(t, strings.Count(err.Error(), "\n"), 2) @@ -112,20 +112,20 @@ func TestCascade(t *testing.T) { t.Run("Context Canceled", func(t *testing.T) { ctx, cancel := context.WithCancel(ctx) cancel() - getters := []share.Getter{ctxGetter, ctxGetter, ctxGetter} + getters := []shwap.Getter{ctxGetter, ctxGetter, ctxGetter} _, err := cascadeGetters(ctx, getters, get) assert.Error(t, err) assert.Equal(t, strings.Count(err.Error(), "\n"), 0) }) t.Run("Single", func(t *testing.T) { - getters := []share.Getter{successGetter} + getters := []shwap.Getter{successGetter} _, err := cascadeGetters(ctx, getters, get) assert.NoError(t, err) }) t.Run("Stuck getter", func(t *testing.T) { - getters := []share.Getter{stuckGetter, successGetter} + getters := []shwap.Getter{stuckGetter, successGetter} _, err := cascadeGetters(ctx, getters, get) assert.NoError(t, err) }) diff --git a/share/shwap/getters/mock/getter.go b/share/shwap/getters/mock/getter.go new file mode 100644 index 0000000000..ade35b39f8 --- /dev/null +++ b/share/shwap/getters/mock/getter.go @@ -0,0 +1,99 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/celestiaorg/celestia-node/share/shwap (interfaces: Getter) + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + header "github.com/celestiaorg/celestia-node/header" + shwap "github.com/celestiaorg/celestia-node/share/shwap" + share "github.com/celestiaorg/go-square/v2/share" + rsmt2d "github.com/celestiaorg/rsmt2d" + gomock "github.com/golang/mock/gomock" +) + +// MockGetter is a mock of Getter interface. +type MockGetter struct { + ctrl *gomock.Controller + recorder *MockGetterMockRecorder +} + +// MockGetterMockRecorder is the mock recorder for MockGetter. +type MockGetterMockRecorder struct { + mock *MockGetter +} + +// NewMockGetter creates a new mock instance. +func NewMockGetter(ctrl *gomock.Controller) *MockGetter { + mock := &MockGetter{ctrl: ctrl} + mock.recorder = &MockGetterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGetter) EXPECT() *MockGetterMockRecorder { + return m.recorder +} + +// GetEDS mocks base method. +func (m *MockGetter) GetEDS(arg0 context.Context, arg1 *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEDS", arg0, arg1) + ret0, _ := ret[0].(*rsmt2d.ExtendedDataSquare) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetEDS indicates an expected call of GetEDS. +func (mr *MockGetterMockRecorder) GetEDS(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEDS", reflect.TypeOf((*MockGetter)(nil).GetEDS), arg0, arg1) +} + +// GetNamespaceData mocks base method. +func (m *MockGetter) GetNamespaceData(arg0 context.Context, arg1 *header.ExtendedHeader, arg2 share.Namespace) (shwap.NamespaceData, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNamespaceData", arg0, arg1, arg2) + ret0, _ := ret[0].(shwap.NamespaceData) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetNamespaceData indicates an expected call of GetNamespaceData. +func (mr *MockGetterMockRecorder) GetNamespaceData(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNamespaceData", reflect.TypeOf((*MockGetter)(nil).GetNamespaceData), arg0, arg1, arg2) +} + +// GetRow mocks base method. +func (m *MockGetter) GetRow(arg0 context.Context, arg1 *header.ExtendedHeader, arg2 int) (shwap.Row, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRow", arg0, arg1, arg2) + ret0, _ := ret[0].(shwap.Row) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRow indicates an expected call of GetRow. +func (mr *MockGetterMockRecorder) GetRow(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRow", reflect.TypeOf((*MockGetter)(nil).GetRow), arg0, arg1, arg2) +} + +// GetSamples mocks base method. +func (m *MockGetter) GetSamples(arg0 context.Context, arg1 *header.ExtendedHeader, arg2 []shwap.SampleCoords) ([]shwap.Sample, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSamples", arg0, arg1, arg2) + ret0, _ := ret[0].([]shwap.Sample) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSamples indicates an expected call of GetSamples. +func (mr *MockGetterMockRecorder) GetSamples(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSamples", reflect.TypeOf((*MockGetter)(nil).GetSamples), arg0, arg1, arg2) +} diff --git a/share/shwap/getters/testing.go b/share/shwap/getters/testing.go new file mode 100644 index 0000000000..c244204aba --- /dev/null +++ b/share/shwap/getters/testing.go @@ -0,0 +1,105 @@ +package getters + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/celestia-app/v3/pkg/da" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/header" + "github.com/celestiaorg/celestia-node/header/headertest" + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +// TestGetter provides a testing SingleEDSGetter and the root of the EDS it holds. +func TestGetter(t *testing.T) (shwap.Getter, *header.ExtendedHeader) { + square := edstest.RandEDS(t, 8) + roots, err := share.NewAxisRoots(square) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + require.NoError(t, err) + return &SingleEDSGetter{ + EDS: eds.Rsmt2D{ExtendedDataSquare: square}, + }, eh +} + +// SingleEDSGetter contains a single EDS where data is retrieved from. +// Its primary use is testing, and GetNamespaceData is not supported. +type SingleEDSGetter struct { + EDS eds.Rsmt2D +} + +// GetSamples get samples from a kept EDS if exist and if the correct root is given. +func (seg *SingleEDSGetter) GetSamples(ctx context.Context, hdr *header.ExtendedHeader, + indices []shwap.SampleCoords, +) ([]shwap.Sample, error) { + err := seg.checkRoots(hdr.DAH) + if err != nil { + return nil, err + } + + smpls := make([]shwap.Sample, len(indices)) + for i, idx := range indices { + smpl, err := seg.EDS.Sample(ctx, idx) + if err != nil { + return nil, err + } + + smpls[i] = smpl + } + + return smpls, nil +} + +func (seg *SingleEDSGetter) GetRow( + ctx context.Context, + header *header.ExtendedHeader, + rowIdx int, +) (shwap.Row, error) { + err := seg.checkRoots(header.DAH) + if err != nil { + return shwap.Row{}, err + } + + axisHalf, err := seg.EDS.AxisHalf(ctx, rsmt2d.Row, rowIdx) + if err != nil { + return shwap.Row{}, err + } + return axisHalf.ToRow(), nil +} + +// GetEDS returns a kept EDS if the correct root is given. +func (seg *SingleEDSGetter) GetEDS( + _ context.Context, + header *header.ExtendedHeader, +) (*rsmt2d.ExtendedDataSquare, error) { + err := seg.checkRoots(header.DAH) + if err != nil { + return nil, err + } + return seg.EDS.ExtendedDataSquare, nil +} + +// GetNamespaceData returns NamespacedShares from a kept EDS if the correct root is given. +func (seg *SingleEDSGetter) GetNamespaceData(context.Context, *header.ExtendedHeader, libshare.Namespace, +) (shwap.NamespaceData, error) { + panic("SingleEDSGetter: GetNamespaceData is not implemented") +} + +func (seg *SingleEDSGetter) checkRoots(roots *share.AxisRoots) error { + dah, err := da.NewDataAvailabilityHeader(seg.EDS.ExtendedDataSquare) + if err != nil { + return err + } + if !roots.Equals(&dah) { + return fmt.Errorf("unknown EDS: have %s, asked %s", dah.String(), roots.String()) + } + return nil +} diff --git a/share/shwap/namespace_data.go b/share/shwap/namespace_data.go new file mode 100644 index 0000000000..1cc9078d18 --- /dev/null +++ b/share/shwap/namespace_data.go @@ -0,0 +1,85 @@ +package shwap + +import ( + "errors" + "fmt" + "io" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" +) + +// NamespaceDataName is the name identifier for the namespace data container. +const NamespaceDataName = "nd_v0" + +// NamespaceData stores collections of RowNamespaceData, each representing shares and their proofs +// within a namespace. +// NOTE: NamespaceData does not have it protobuf Container representation and its only *streamed* +// as RowNamespaceData. The protobuf might be added as need comes. +type NamespaceData []RowNamespaceData + +// Flatten combines all shares from all rows within the namespace into a single slice. +func (nd NamespaceData) Flatten() []libshare.Share { + var shares []libshare.Share + for _, row := range nd { + shares = append(shares, row.Shares...) + } + return shares +} + +// Verify checks the integrity of the NamespaceData against a provided root and namespace. +func (nd NamespaceData) Verify(root *share.AxisRoots, namespace libshare.Namespace) error { + rowIdxs, err := share.RowsWithNamespace(root, namespace) + if err != nil { + return err + } + if len(rowIdxs) != len(nd) { + return fmt.Errorf("expected %d rows, found %d rows", len(rowIdxs), len(nd)) + } + + for i, row := range nd { + if err := row.Verify(root, namespace, rowIdxs[i]); err != nil { + return fmt.Errorf("validating row: %w", err) + } + } + return nil +} + +// ReadFrom reads NamespaceData from the provided reader implementing io.ReaderFrom. +// It reads series of length-delimited RowNamespaceData until EOF draining the stream. +func (nd *NamespaceData) ReadFrom(reader io.Reader) (int64, error) { + var ndNew []RowNamespaceData + var n int64 + for { + var rnd RowNamespaceData + nn, err := rnd.ReadFrom(reader) + n += nn + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return n, err + } + + ndNew = append(ndNew, rnd) + } + + // all rows have been read + *nd = ndNew + return n, nil +} + +// WriteTo writes the length-delimited protobuf of NamespaceData to the provided writer. +// implementing io.WriterTo. +func (nd NamespaceData) WriteTo(writer io.Writer) (int64, error) { + var n int64 + for _, rnd := range nd { + nn, err := rnd.WriteTo(writer) + n += nn + if err != nil { + return n, err + } + } + return n, nil +} diff --git a/share/shwap/namespace_data_id.go b/share/shwap/namespace_data_id.go new file mode 100644 index 0000000000..2af86858c0 --- /dev/null +++ b/share/shwap/namespace_data_id.go @@ -0,0 +1,125 @@ +package shwap + +import ( + "fmt" + "io" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +// NamespaceDataIDSize defines the total size of a NamespaceDataID in bytes, combining the +// size of a EdsID and the size of a Namespace. +const NamespaceDataIDSize = EdsIDSize + libshare.NamespaceSize + +// NamespaceDataID filters the data in the EDS by a specific namespace. +type NamespaceDataID struct { + // Embedding EdsID to include the block height. + EdsID + // DataNamespace will be used to identify the data within the EDS. + DataNamespace libshare.Namespace +} + +// NewNamespaceDataID creates a new NamespaceDataID with the specified parameters. It +// validates the namespace and returns an error if it is invalid. +func NewNamespaceDataID(height uint64, namespace libshare.Namespace) (NamespaceDataID, error) { + ndid := NamespaceDataID{ + EdsID: EdsID{ + Height: height, + }, + DataNamespace: namespace, + } + + if err := ndid.Validate(); err != nil { + return NamespaceDataID{}, err + } + return ndid, nil +} + +// NamespaceDataIDFromBinary deserializes a NamespaceDataID from its binary form. It returns +// an error if the binary data's length does not match the expected size. +func NamespaceDataIDFromBinary(data []byte) (NamespaceDataID, error) { + if len(data) != NamespaceDataIDSize { + return NamespaceDataID{}, + fmt.Errorf("invalid NamespaceDataID length: expected %d, got %d", NamespaceDataIDSize, len(data)) + } + + edsID, err := EdsIDFromBinary(data[:EdsIDSize]) + if err != nil { + return NamespaceDataID{}, fmt.Errorf("error unmarshaling EDSID: %w", err) + } + + ns, err := libshare.NewNamespaceFromBytes(data[EdsIDSize:]) + if err != nil { + return NamespaceDataID{}, fmt.Errorf("error unmarshaling namespace: %w", err) + } + + ndid := NamespaceDataID{ + EdsID: edsID, + DataNamespace: ns, + } + if err := ndid.Validate(); err != nil { + return NamespaceDataID{}, err + } + return ndid, nil +} + +// Equals checks equality of NamespaceDataID. +func (ndid *NamespaceDataID) Equals(other NamespaceDataID) bool { + return ndid.EdsID.Equals(other.EdsID) && ndid.DataNamespace.Equals(other.DataNamespace) +} + +// ReadFrom reads the binary form of NamespaceDataID from the provided reader. +func (ndid *NamespaceDataID) ReadFrom(r io.Reader) (int64, error) { + data := make([]byte, NamespaceDataIDSize) + n, err := io.ReadFull(r, data) + if err != nil { + return int64(n), err + } + if n != NamespaceDataIDSize { + return int64(n), fmt.Errorf("NamespaceDataID: expected %d bytes, got %d", NamespaceDataIDSize, n) + } + id, err := NamespaceDataIDFromBinary(data) + if err != nil { + return int64(n), fmt.Errorf("NamespaceDataIDFromBinary: %w", err) + } + *ndid = id + return int64(n), nil +} + +// MarshalBinary encodes NamespaceDataID into binary form. +// NOTE: Proto is avoided because +// * Its size is not deterministic which is required for IPLD. +// * No support for uint16 +func (ndid NamespaceDataID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, NamespaceDataIDSize) + return ndid.appendTo(data), nil +} + +// WriteTo writes the binary form of NamespaceDataID to the provided writer. +func (ndid NamespaceDataID) WriteTo(w io.Writer) (int64, error) { + data, err := ndid.MarshalBinary() + if err != nil { + return 0, err + } + n, err := w.Write(data) + return int64(n), err +} + +// Validate checks if the NamespaceDataID is valid. It checks the validity of the EdsID and the +// DataNamespace. +func (ndid NamespaceDataID) Validate() error { + if err := ndid.EdsID.Validate(); err != nil { + return fmt.Errorf("validating RowID: %w", err) + } + + if err := ndid.DataNamespace.ValidateForData(); err != nil { + return fmt.Errorf("%w: validating DataNamespace: %w", ErrInvalidID, err) + } + return nil +} + +// appendTo helps in appending the binary form of DataNamespace to the serialized RowID data. +func (ndid NamespaceDataID) appendTo(data []byte) []byte { + data = ndid.EdsID.appendTo(data) + return append(data, ndid.DataNamespace.Bytes()...) +} diff --git a/share/shwap/namespace_data_id_test.go b/share/shwap/namespace_data_id_test.go new file mode 100644 index 0000000000..c30f75b3bc --- /dev/null +++ b/share/shwap/namespace_data_id_test.go @@ -0,0 +1,48 @@ +package shwap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +func TestNamespaceDataID(t *testing.T) { + ns := libshare.RandomNamespace() + + id, err := NewNamespaceDataID(1, ns) + require.NoError(t, err) + + data, err := id.MarshalBinary() + require.NoError(t, err) + + sidOut, err := NamespaceDataIDFromBinary(data) + require.NoError(t, err) + assert.EqualValues(t, id, sidOut) + + err = sidOut.Validate() + require.NoError(t, err) + require.True(t, id.Equals(sidOut)) +} + +func TestNamespaceDataIDReaderWriter(t *testing.T) { + ns := libshare.RandomNamespace() + + id, err := NewNamespaceDataID(1, ns) + require.NoError(t, err) + + buf := bytes.NewBuffer(nil) + n, err := id.WriteTo(buf) + require.NoError(t, err) + require.Equal(t, int64(NamespaceDataIDSize), n) + + ndidOut := NamespaceDataID{} + n, err = ndidOut.ReadFrom(buf) + require.NoError(t, err) + require.Equal(t, int64(NamespaceDataIDSize), n) + + require.EqualValues(t, id, ndidOut) +} diff --git a/share/shwap/p2p/bitswap/bitswap.go b/share/shwap/p2p/bitswap/bitswap.go new file mode 100644 index 0000000000..a243f9b492 --- /dev/null +++ b/share/shwap/p2p/bitswap/bitswap.go @@ -0,0 +1,205 @@ +package bitswap + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/ipfs/boxo/bitswap/client" + "github.com/ipfs/boxo/bitswap/network" + "github.com/ipfs/boxo/bitswap/server" + "github.com/ipfs/boxo/blockstore" + blocks "github.com/ipfs/go-block-format" + delay "github.com/ipfs/go-ipfs-delay" + routinghelpers "github.com/libp2p/go-libp2p-routing-helpers" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/protocol" +) + +// THESE VALUES HAVE TO BE REVISED ON EVERY SINGLE MAX BLOCK SIZE BUMP +// CURRENT BLOCK SIZE TARGET: 8MB (256 EDS) + +// Client constants +const ( + // simulateDontHaves emulates DONT_HAVE message from a peer after dynamically estimated timeout. + // Simulating protects from malicious peers and ensure Bitswap tries new peers if originally + // selected one is slow or not responding. + // Dynamic timeout, in our case, can be treated as optimization allowing to move on a next peer faster. + // See simulateDontHaveConfig for more details. + simulateDontHaves = true + // broadcastDelay defines the initial delay before Bitswap client starts aggressive + // broadcasting of live WANTs to all the peers. We offset this for longer than the default to minimize + // unnecessary broadcasting as in most cases we already have peers connected with needed data on + // a new request. + broadcastDelay = time.Second * 10 + // disablePerPeerRetries disables rebroadcasting of WANTs with no response in peer message queue. + // We keep it enabled to account for case where maxServerWantListsPerPeer gets exceeded, loosing WANTs. + disablePerPeerRetries = false + // provSearchDelay is similar to the broadcastDelay, but it targets DHT/ContentRouting + // peer discovery and a gentle broadcast of a single random live WANT to all connected peers. + // Considering no DHT usage and broadcasting configured by broadcastDelay, we set + // provSearchDelay to max value, effectively disabling it + provSearchDelay = 1<<63 - 1 +) + +// Server constants +const ( + // sendDontHaves dictates Bitswap Server to send DONT_HAVE messages to peers when they requested block is not + // available locally. This is useful for clients to quickly move on to another peer. + // This breaks reconstruction, unless we make reconstruction case detectable on the Server side blocking Bitswap + // from serving DONT_HAVE messages in Blockstore, which would be the goal. + // TODO(@Wondertan): enable once Blockstore handles recent blocks + sendDontHaves = false + // maxServerWantListsPerPeer defines the limit for maximum possible cached wants/requests per peer + // in the Bitswap. Exceeding this limit will cause Bitswap server to drop requested WANTs leaving + // client stuck for some time. + // This is relevant until https://github.com/ipfs/boxo/pull/629#discussion_r1653362485 is fixed. + // 1024 is 64 sampling requests of size 16 and 8 EDS requests with 8mb blocks + maxServerWantListsPerPeer = 4096 + // targetResponseSize defines soft-limit of how much data server packs into a response. + // More data means more compute and time spend while serving a single peer under load, and thus increasing serving + // latency for other peers. + // We set it to 65KiB, which fits a Row of 8MB block with additional metadata. + targetResponseSize = 65 << 10 + // responseWorkersCount is the number of workers packing responses on the server. + // More workers mean more parallelism and faster responses, but also more memory usage. + // Default is 8. + responseWorkersCount = 32 + // maxWorkPerPeer sets maximum concurrent processing work allowed for a peer. + // We set it to be equal to targetResponseSize * responseWorkersCount, so a single peer + // can utilize all workers. Note that when there are more peers, prioritization still ensures + // even spread across all peers. + maxWorkPerPeer = targetResponseSize * responseWorkersCount + // replaceHasWithBlockMaxSize configures Bitswap to use Has method instead of GetSize to check existence + // of a CID in Blockstore. + replaceHasWithBlockMaxSize = 0 + // providesEnabled dictates Bitswap Server not to provide content to DHT/ContentRouting as we don't use it + providesEnabled = false +) + +// simulateDontHaveConfig contains the configuration for Bitswap's DONT_HAVE simulation. +// This relies on latency to determine when to simulate a DONT_HAVE from a peer. +var simulateDontHaveConfig = &client.DontHaveTimeoutConfig{ + // MaxTimeout is the limit cutoff time for the dynamic timeout estimation. + MaxTimeout: 30 * time.Second, + // MinTimeout is the minimum timeout for the dynamic timeout estimation. + MinTimeout: 1 * time.Second, + // MessageLatencyMultiplier is the multiplier for the message latency to account for errors + // THIS IS THE MOST IMPORTANT KNOB and is tricky to get right due to high variance in latency + // and particularly request processing time. + MessageLatencyMultiplier: 4, + // MessageLatencyAlpha is the alpha value for the exponential moving average of message latency. + // It's a 0-1 value. More prefers recent latencies, less prefers older measurements. + MessageLatencyAlpha: 0.2, + + // Below are less important knobs and target initial timeouts for when there is no latency data yet + // + // DontHaveTimeout is default timeout used until ping latency is measured + DontHaveTimeout: 10 * time.Second, + // time estimate for how long it takes to process a WANT/Block message + // used for ping timeout calculation + MaxExpectedWantProcessTime: 7 * time.Second, + // multiplier to account for errors with ping latency estimates + PingLatencyMultiplier: 3, +} + +// NewNetwork constructs Bitswap network for Shwap protocol composition. +func NewNetwork(host host.Host, prefix protocol.ID) network.BitSwapNetwork { + prefix = shwapProtocolID(prefix) + net := network.NewFromIpfsHost( + host, + routinghelpers.Null{}, + network.Prefix(prefix), + network.SupportedProtocols([]protocol.ID{protocolID}), + ) + return net +} + +// NewClient constructs a Bitswap client with parameters optimized for Shwap protocol composition. +// Meant to be used by Full and Light nodes. +func NewClient( + ctx context.Context, + net network.BitSwapNetwork, + bstore blockstore.Blockstore, +) *client.Client { + opts := []client.Option{ + client.SetSimulateDontHavesOnTimeout(simulateDontHaves), + client.WithDontHaveTimeoutConfig(simulateDontHaveConfig), + client.WithDisabledMessageQueueRebroadcast(disablePerPeerRetries), + // Prevents Has calls to Blockstore for metric that counts duplicates + // Unnecessary for our use case, so we can save some disk lookups. + client.WithoutDuplicatedBlockStats(), + + // These two options have mixed up named. One should be another and vice versa. + client.ProviderSearchDelay(broadcastDelay), + client.RebroadcastDelay(delay.Fixed(provSearchDelay)), + } + return client.New( + ctx, + net, + bstore, + opts..., + ) +} + +// NewServer construct a Bitswap server with parameters optimized for Shwap protocol composition. +// Meant to be used by Full nodes. +func NewServer( + ctx context.Context, + net network.BitSwapNetwork, + bstore blockstore.Blockstore, +) *server.Server { + opts := []server.Option{ + server.ProvideEnabled(providesEnabled), + server.SetSendDontHaves(sendDontHaves), + server.MaxQueuedWantlistEntriesPerPeer(maxServerWantListsPerPeer), + server.WithTargetMessageSize(targetResponseSize), + server.MaxOutstandingBytesPerPeer(maxWorkPerPeer), + server.WithWantHaveReplaceSize(replaceHasWithBlockMaxSize), + server.TaskWorkerCount(responseWorkersCount), + } + return server.New(ctx, net, bstore, opts...) +} + +type Bitswap struct { + *client.Client + *server.Server +} + +func New( + ctx context.Context, + net network.BitSwapNetwork, + bstore blockstore.Blockstore, +) *Bitswap { + return &Bitswap{ + Client: NewClient(ctx, net, bstore), + Server: NewServer(ctx, net, bstore), + } +} + +func (bs *Bitswap) NotifyNewBlocks(ctx context.Context, blks ...blocks.Block) error { + return errors.Join( + bs.Client.NotifyNewBlocks(ctx, blks...), + bs.Server.NotifyNewBlocks(ctx, blks...), + ) +} + +func (bs *Bitswap) Close() error { + bs.Server.Close() + return bs.Client.Close() +} + +// TODO(@Wondertan): We have to use the protocol defined by Bitswap here +// +// due to a little bug. Bitswap allows setting custom protocols, but +// they have to be either one of the switch. +// https://github.com/ipfs/boxo/blob/dfd4a53ba828a368cec8d61c3fe12969ac6aa94c/bitswap/network/ipfs_impl.go#L250-L266 +var protocolID = network.ProtocolBitswap + +func shwapProtocolID(network protocol.ID) protocol.ID { + if network == "" { + return "" + } + return protocol.ID(fmt.Sprintf("%s/shwap", network)) +} diff --git a/share/shwap/p2p/bitswap/block.go b/share/shwap/p2p/bitswap/block.go new file mode 100644 index 0000000000..940afb5bc2 --- /dev/null +++ b/share/shwap/p2p/bitswap/block.go @@ -0,0 +1,38 @@ +package bitswap + +import ( + "context" + + "github.com/ipfs/go-cid" + logger "github.com/ipfs/go-log/v2" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" +) + +var log = logger.Logger("shwap/bitswap") + +// Block represents Bitswap compatible generalization over Shwap containers. +// All Shwap containers must have a registered wrapper +// implementing the interface in order to be compatible with Bitswap. +// NOTE: This is not a Blockchain block, but an IPFS/Bitswap block. +type Block interface { + // CID returns Shwap ID of the Block formatted as CID. + CID() cid.Cid + // Height reports the Height of the Shwap container behind the Block. + Height() uint64 + + // Populate fills up the Block with the Shwap container getting it out of the EDS + // Accessor. + Populate(context.Context, eds.Accessor) error + // Marshal serializes bytes of the Shwap Container the Block holds. + // MUST exclude the Shwap ID. + Marshal() ([]byte, error) + // UnmarshalFn returns closure that unmarshal the Block with the Shwap container. + // Unmarshalling involves data validation against the given AxisRoots. + UnmarshalFn(*share.AxisRoots) UnmarshalFn +} + +// UnmarshalFn is a closure produced by a Block that unmarshalls and validates +// the given serialized bytes of a Shwap container with ID and populates the Block with it on success. +type UnmarshalFn func(container, id []byte) error diff --git a/share/shwap/p2p/bitswap/block_fetch.go b/share/shwap/p2p/bitswap/block_fetch.go new file mode 100644 index 0000000000..94fe5dbdac --- /dev/null +++ b/share/shwap/p2p/bitswap/block_fetch.go @@ -0,0 +1,271 @@ +package bitswap + +import ( + "context" + "crypto/sha256" + "fmt" + "sync" + + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + + "github.com/celestiaorg/celestia-node/share" +) + +// WithFetcher instructs [Fetch] to use the given Fetcher. +// Useful for reusable Fetcher sessions. +func WithFetcher(session exchange.Fetcher) FetchOption { + return func(options *fetchOptions) { + options.Session = session + } +} + +// WithStore instructs [Fetch] to store all the fetched Blocks into the given Blockstore. +func WithStore(store blockstore.Blockstore) FetchOption { + return func(options *fetchOptions) { + options.Store = store + } +} + +// Fetch fetches and populates given Blocks using Fetcher wrapping Bitswap. +// +// Validates Block against the given AxisRoots and skips Blocks that are already populated. +// Gracefully synchronize identical Blocks requested simultaneously. +// Blocks until either context is canceled or all Blocks are fetched and populated. +func Fetch( + ctx context.Context, + exchg exchange.Interface, + root *share.AxisRoots, + blks []Block, + opts ...FetchOption, +) error { + var from, to int + for to < len(blks) { + from, to = to, to+maxServerWantListsPerPeer + if to >= len(blks) { + to = len(blks) + } + + err := fetch(ctx, exchg, root, blks[from:to], opts...) + if err != nil { + return err + } + } + + return ctx.Err() +} + +// fetch fetches given Blocks. +// See [Fetch] for detailed description. +func fetch( + ctx context.Context, + exchg exchange.Interface, + root *share.AxisRoots, + blks []Block, + opts ...FetchOption, +) error { + var options fetchOptions + for _, opt := range opts { + opt(&options) + } + + fetcher := options.getFetcher(exchg) + cids := make([]cid.Cid, 0, len(blks)) + duplicates := make(map[cid.Cid]Block) + for _, blk := range blks { + cid := blk.CID() // memoize CID for reuse as it ain't free + cids = append(cids, cid) + + // store the UnmarshalFn s.t. hasher can access it + // and fill in the Block + unmarshalFn := blk.UnmarshalFn(root) + _, exists := unmarshalFns.LoadOrStore(cid, &unmarshalEntry{UnmarshalFn: unmarshalFn}) + if exists { + // the unmarshalFn has already been stored for the cid + // means there is ongoing fetch happening for the same cid + duplicates[cid] = blk // so mark the Block as duplicate + } else { + // cleanup are by the original requester and + // only after we are sure we got the block + defer unmarshalFns.Delete(cid) + } + } + + blkCh, err := fetcher.GetBlocks(ctx, cids) + if err != nil { + return fmt.Errorf("requesting Bitswap blocks: %w", err) + } + + for bitswapBlk := range blkCh { // GetBlocks closes blkCh on ctx cancellation + // NOTE: notification for duplicates is on purpose and to cover a flaky case + // It's harmless in practice to do additional notifications in case of duplicates + if err := exchg.NotifyNewBlocks(ctx, bitswapBlk); err != nil { + log.Error("failed to notify the new Bitswap block: %s", err) + } + + blk, ok := duplicates[bitswapBlk.Cid()] + if ok { + // uncommon duplicate case: concurrent fetching of the same block. + // The block hasn't been invoked inside hasher verification, + // so we have to unmarshal it ourselves. + unmarshalFn := blk.UnmarshalFn(root) + err := unmarshal(unmarshalFn, bitswapBlk.RawData()) + if err != nil { + // this means verification succeeded in the hasher but failed here + // this case should never happen in practice + // and if so something is really wrong + panic(fmt.Sprintf("unmarshaling duplicate block: %s", err)) + } + // NOTE: This approach has a downside that we redo deserialization and computationally + // expensive computation for as many duplicates. We tried solutions that doesn't have this + // problem, but they are *much* more complex. Considering this a rare edge-case the tradeoff + // towards simplicity has been made. + continue + } + // common case: the block was populated by the hasher + // so store it if requested + err := options.store(ctx, bitswapBlk) + if err != nil { + log.Error("failed to store the new Bitswap block: %s", err) + } + } + + return ctx.Err() +} + +// unmarshal unmarshalls the Shwap Container data into a Block with the given UnmarshalFn +func unmarshal(unmarshalFn UnmarshalFn, data []byte) error { + cid, containerData, err := unmarshalProto(data) + if err != nil { + return err + } + + id, err := extractFromCID(cid) + if err != nil { + return err + } + + err = unmarshalFn(containerData, id) + if err != nil { + return fmt.Errorf("verifying and unmarshalling container data: %w", err) + } + + return nil +} + +// unmarshalFns exist to communicate between Fetch and hasher, and it's global as a necessity +// +// Fetch registers UnmarshalFNs that hasher then uses to validate and unmarshal Block responses coming +// through Bitswap +// +// Bitswap does not provide *stateful* verification out of the box and by default +// messages are verified by their respective MultiHashes that are registered globally. +// For every Block type there is a global hasher registered that accesses stored UnmarshalFn once a +// message is received. It then uses UnmarshalFn to validate and fill in the respective Block +// +// sync.Map is used to minimize contention for disjoint keys +var unmarshalFns sync.Map + +// unmarshalEntry wraps UnmarshalFn with a mutex to protect it from concurrent access. +type unmarshalEntry struct { + sync.Mutex + UnmarshalFn +} + +// hasher implements hash.Hash to be registered as custom multihash +// hasher is the *hack* to inject custom verification logic into Bitswap +type hasher struct { + // IDSize of the respective Shwap container + IDSize int // to be set during hasher registration + + sum []byte +} + +func (h *hasher) Write(data []byte) (int, error) { + err := h.write(data) + if err != nil { + err = fmt.Errorf("hasher: %w", err) + log.Warn(err) + return 0, fmt.Errorf("shwap/bitswap: %w", err) + } + + return len(data), nil +} + +func (h *hasher) write(data []byte) error { + cid, container, err := unmarshalProto(data) + if err != nil { + return fmt.Errorf("unmarshalling proto: %w", err) + } + + // get ID out of CID while validating it + id, err := extractFromCID(cid) + if err != nil { + return err + } + + // get registered UnmarshalFn and use it to check data validity and + // pass it to Fetch caller + val, ok := unmarshalFns.Load(cid) + if !ok { + return fmt.Errorf("no unmarshallers registered for %s", cid.String()) + } + entry := val.(*unmarshalEntry) + + // ensure UnmarshalFn is synchronized + // NOTE: Bitswap may call hasher.Write concurrently, which may call unmarshall concurrently + // this we need this synchronization. + entry.Lock() + err = entry.UnmarshalFn(container, id) + if err != nil { + return fmt.Errorf("verifying and unmarshalling container data: %w", err) + } + entry.Unlock() + + // set the id as resulting sum + // it's required for the sum to match the requested ID + // to satisfy hash contract and signal to Bitswap that data is correct + h.sum = id + return nil +} + +func (h *hasher) Sum([]byte) []byte { + return h.sum +} + +func (h *hasher) Reset() { + h.sum = nil +} + +func (h *hasher) Size() int { + return h.IDSize +} + +func (h *hasher) BlockSize() int { + return sha256.BlockSize +} + +type FetchOption func(*fetchOptions) + +type fetchOptions struct { + Session exchange.Fetcher + Store blockstore.Blockstore +} + +func (options *fetchOptions) getFetcher(exhng exchange.Interface) exchange.Fetcher { + if options.Session != nil { + return options.Session + } + + return exhng +} + +func (options *fetchOptions) store(ctx context.Context, blk blocks.Block) error { + if options.Store == nil { + return nil + } + + return options.Store.Put(ctx, blk) +} diff --git a/share/shwap/p2p/bitswap/block_fetch_test.go b/share/shwap/p2p/bitswap/block_fetch_test.go new file mode 100644 index 0000000000..05b0c78b19 --- /dev/null +++ b/share/shwap/p2p/bitswap/block_fetch_test.go @@ -0,0 +1,179 @@ +package bitswap + +import ( + "context" + "math/rand/v2" + "sync" + "testing" + "time" + + "github.com/ipfs/boxo/bitswap/client" + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + "github.com/libp2p/go-libp2p/core/host" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share/eds" +) + +func TestFetch_Options(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + const items = 128 + bstore, cids := testBlockstore(ctx, t, items) + + t.Run("WithBlockstore", func(t *testing.T) { + exchange := newExchange(ctx, t, bstore) + + blks := make([]Block, 0, cids.Len()) + _ = cids.ForEach(func(c cid.Cid) error { + blk, err := newEmptyTestBlock(c) + require.NoError(t, err) + blks = append(blks, blk) + return nil + }) + + bstore := blockstore.NewBlockstore(ds.NewMapDatastore()) + err := Fetch(ctx, exchange, nil, blks, WithStore(bstore)) + require.NoError(t, err) + + for _, blk := range blks { + ok, err := bstore.Has(ctx, blk.CID()) + require.NoError(t, err) + require.True(t, ok) + } + }) + + t.Run("WithFetcher", func(t *testing.T) { + exchange := newExchange(ctx, t, bstore) + + blks := make([]Block, 0, cids.Len()) + _ = cids.ForEach(func(c cid.Cid) error { + blk, err := newEmptyTestBlock(c) + require.NoError(t, err) + blks = append(blks, blk) + return nil + }) + + session := exchange.NewSession(ctx) + fetcher := &testFetcher{Embedded: session} + err := Fetch(ctx, exchange, nil, blks, WithFetcher(fetcher)) + require.NoError(t, err) + require.Equal(t, len(blks), fetcher.Fetched) + }) +} + +func TestFetch_Duplicates(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + const items = 128 + bstore, cids := testBlockstore(ctx, t, items) + exchange := newExchange(ctx, t, bstore) + + var wg sync.WaitGroup + for i := range items { + blks := make([]Block, 0, cids.Len()) + _ = cids.ForEach(func(c cid.Cid) error { + blk, err := newEmptyTestBlock(c) + require.NoError(t, err) + blks = append(blks, blk) + return nil + }) + + wg.Add(1) + go func(i int) { + rint := rand.IntN(10) + // this sleep ensures fetches aren't started simultaneously, allowing to check for edge-cases + time.Sleep(time.Millisecond * time.Duration(rint)) + + err := Fetch(ctx, exchange, nil, blks) + assert.NoError(t, err) + wg.Done() + }(i) + } + wg.Wait() + + var entries int + unmarshalFns.Range(func(key, _ any) bool { + unmarshalFns.Delete(key) + entries++ + return true + }) + require.Zero(t, entries) +} + +func newExchangeOverEDS(ctx context.Context, t *testing.T, rsmt2d *rsmt2d.ExtendedDataSquare) exchange.SessionExchange { + bstore := &Blockstore{ + Getter: testAccessorGetter{ + AccessorStreamer: &eds.Rsmt2D{ExtendedDataSquare: rsmt2d}, + }, + } + return newExchange(ctx, t, bstore) +} + +func newExchange(ctx context.Context, t *testing.T, bstore blockstore.Blockstore) exchange.SessionExchange { + net, err := mocknet.FullMeshLinked(3) + require.NoError(t, err) + + newServer(ctx, net.Hosts()[0], bstore) + newServer(ctx, net.Hosts()[1], bstore) + + client := newClient(ctx, net.Hosts()[2], bstore) + + err = net.ConnectAllButSelf() + require.NoError(t, err) + return client +} + +func newServer(ctx context.Context, host host.Host, store blockstore.Blockstore) { + net := NewNetwork(host, "test") + server := NewServer( + ctx, + net, + store, + ) + net.Start(server) +} + +func newClient(ctx context.Context, host host.Host, store blockstore.Blockstore) *client.Client { + net := NewNetwork(host, "test") + client := NewClient(ctx, net, store) + net.Start(client) + return client +} + +type testAccessorGetter struct { + eds.AccessorStreamer +} + +func (t testAccessorGetter) GetByHeight(context.Context, uint64) (eds.AccessorStreamer, error) { + return t.AccessorStreamer, nil +} + +func (t testAccessorGetter) HasByHeight(context.Context, uint64) (bool, error) { + return true, nil +} + +type testFetcher struct { + Fetched int + + Embedded exchange.Fetcher +} + +func (t *testFetcher) GetBlock(context.Context, cid.Cid) (blocks.Block, error) { + panic("not implemented") +} + +func (t *testFetcher) GetBlocks(ctx context.Context, cids []cid.Cid) (<-chan blocks.Block, error) { + t.Fetched += len(cids) + return t.Embedded.GetBlocks(ctx, cids) +} diff --git a/share/shwap/p2p/bitswap/block_proto.go b/share/shwap/p2p/bitswap/block_proto.go new file mode 100644 index 0000000000..8cab645705 --- /dev/null +++ b/share/shwap/p2p/bitswap/block_proto.go @@ -0,0 +1,46 @@ +package bitswap + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + bitswappb "github.com/celestiaorg/celestia-node/share/shwap/p2p/bitswap/pb" +) + +// marshalProto wraps the given Block in composition protobuf and marshals it. +func marshalProto(blk Block) ([]byte, error) { + containerData, err := blk.Marshal() + if err != nil { + return nil, fmt.Errorf("marshaling Shwap container: %w", err) + } + + blkProto := bitswappb.Block{ + Cid: blk.CID().Bytes(), + Container: containerData, + } + + blkData, err := blkProto.Marshal() + if err != nil { + return nil, fmt.Errorf("marshaling Bitswap Block protobuf: %w", err) + } + + return blkData, nil +} + +// unmarshalProto unwraps given data from composition protobuf and provides +// inner CID and serialized container data. +func unmarshalProto(data []byte) (cid.Cid, []byte, error) { + var blk bitswappb.Block + err := blk.Unmarshal(data) + if err != nil { + return cid.Undef, nil, fmt.Errorf("unmarshalling protobuf block: %w", err) + } + + cid, err := cid.Cast(blk.Cid) + if err != nil { + return cid, nil, fmt.Errorf("casting cid: %w", err) + } + + return cid, blk.Container, nil +} diff --git a/share/shwap/p2p/bitswap/block_registry.go b/share/shwap/p2p/bitswap/block_registry.go new file mode 100644 index 0000000000..f7221df120 --- /dev/null +++ b/share/shwap/p2p/bitswap/block_registry.go @@ -0,0 +1,71 @@ +package bitswap + +import ( + "fmt" + "hash" + + "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" +) + +// EmptyBlock constructs an empty Block with type in the given CID. +func EmptyBlock(cid cid.Cid) (Block, error) { + spec, err := getSpec(cid) + if err != nil { + return nil, err + } + + blk, err := spec.builder(cid) + if err != nil { + return nil, fmt.Errorf("failed to build a Block for %s: %w", spec.String(), err) + } + + return blk, nil +} + +// maxBlockSize returns the maximum size of the Block type in the given CID. +func maxBlockSize(cid cid.Cid) (int, error) { + spec, err := getSpec(cid) + if err != nil { + return 0, err + } + + return spec.maxSize, nil +} + +// registerBlock registers the new Block type and multihash for it. +func registerBlock(mhcode, codec uint64, maxSize, idSize int, bldrFn func(cid.Cid) (Block, error)) { + mh.Register(mhcode, func() hash.Hash { + return &hasher{IDSize: idSize} + }) + specRegistry[codec] = blockSpec{ + idSize: idSize, + maxSize: maxSize, + mhCode: mhcode, + builder: bldrFn, + } +} + +// getSpec returns the blockSpec for the given CID. +func getSpec(cid cid.Cid) (blockSpec, error) { + spec, ok := specRegistry[cid.Type()] + if !ok { + return blockSpec{}, fmt.Errorf("unsupported codec %d", cid.Type()) + } + + return spec, nil +} + +// blockSpec holds constant metadata about particular Block types. +type blockSpec struct { + idSize int + maxSize int + mhCode uint64 + builder func(cid.Cid) (Block, error) +} + +func (spec *blockSpec) String() string { + return fmt.Sprintf("BlockSpec{IDSize: %d, MHCode: %d}", spec.idSize, spec.mhCode) +} + +var specRegistry = make(map[uint64]blockSpec) diff --git a/share/shwap/p2p/bitswap/block_store.go b/share/shwap/p2p/bitswap/block_store.go new file mode 100644 index 0000000000..182731d7d2 --- /dev/null +++ b/share/shwap/p2p/bitswap/block_store.go @@ -0,0 +1,116 @@ +package bitswap + +import ( + "context" + "errors" + "fmt" + + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/store" +) + +// AccessorGetter abstracts storage system that indexes and manages multiple eds.AccessorGetter by +// network height. +type AccessorGetter interface { + // GetByHeight returns an Accessor by its height. + GetByHeight(ctx context.Context, height uint64) (eds.AccessorStreamer, error) + // HasByHeight reports whether an Accessor for the height exists. + HasByHeight(ctx context.Context, height uint64) (bool, error) +} + +// Blockstore implements generalized Bitswap compatible storage over Shwap containers +// that operates with Block and accesses data through AccessorGetter. +type Blockstore struct { + Getter AccessorGetter +} + +func (b *Blockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) { + blk, err := EmptyBlock(cid) + if err != nil { + return nil, err + } + + acc, err := b.Getter.GetByHeight(ctx, blk.Height()) + if errors.Is(err, store.ErrNotFound) { + log.Debugf("no EDS Accessor for height %v found", blk.Height()) + return nil, ipld.ErrNotFound{Cid: cid} + } + if err != nil { + return nil, fmt.Errorf("getting EDS Accessor for height %v: %w", blk.Height(), err) + } + + defer func() { + if err := acc.Close(); err != nil { + log.Warnf("failed to close EDS accessor for height %v: %s", blk.Height(), err) + } + }() + + if err = blk.Populate(ctx, acc); err != nil { + return nil, fmt.Errorf("failed to populate Shwap Block on height %v: %w", blk.Height(), err) + } + + return convertBitswap(blk) +} + +func (b *Blockstore) GetSize(_ context.Context, cid cid.Cid) (int, error) { + // NOTE: Size is used as a weight for the incoming Bitswap requests. Bitswap uses fair scheduling for the requests + // and prioritizes peers with less *active* work. Active work of a peer is a cumulative weight of all the in-progress + // requests. + + // Constant max block size is used instead of factual size. This avoids disk IO but equalizes the weights of the + // requests of the same type. E.g. row of 2MB EDS and row of 8MB EDS will have the same weight. + size, err := maxBlockSize(cid) + if err != nil { + return 0, err + } + + return size, nil +} + +func (b *Blockstore) Has(ctx context.Context, cid cid.Cid) (bool, error) { + blk, err := EmptyBlock(cid) + if err != nil { + return false, err + } + + _, err = b.Getter.HasByHeight(ctx, blk.Height()) + if err != nil { + return false, fmt.Errorf("checking EDS Accessor for height %v: %w", blk.Height(), err) + } + return true, nil +} + +func (b *Blockstore) Put(context.Context, blocks.Block) error { + panic("not implemented") +} + +func (b *Blockstore) PutMany(context.Context, []blocks.Block) error { + panic("not implemented") +} + +func (b *Blockstore) DeleteBlock(context.Context, cid.Cid) error { + panic("not implemented") +} + +func (b *Blockstore) AllKeysChan(context.Context) (<-chan cid.Cid, error) { panic("not implemented") } + +func (b *Blockstore) HashOnRead(bool) { panic("not implemented") } + +// convertBitswap converts and marshals Block to Bitswap Block. +func convertBitswap(blk Block) (blocks.Block, error) { + protoData, err := marshalProto(blk) + if err != nil { + return nil, fmt.Errorf("failed to wrap Block with proto: %w", err) + } + + bitswapBlk, err := blocks.NewBlockWithCid(protoData, blk.CID()) + if err != nil { + return nil, fmt.Errorf("assembling Bitswap block: %w", err) + } + + return bitswapBlk, nil +} diff --git a/share/shwap/p2p/bitswap/block_test.go b/share/shwap/p2p/bitswap/block_test.go new file mode 100644 index 0000000000..a7b7f590a8 --- /dev/null +++ b/share/shwap/p2p/bitswap/block_test.go @@ -0,0 +1,114 @@ +package bitswap + +import ( + "context" + crand "crypto/rand" + "encoding/binary" + "testing" + "time" + + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/go-cid" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" +) + +const ( + testCodec = 0x9999 + testMultihashCode = 0x9999 + testBlockSize = 256 + testIDSize = 2 +) + +func init() { + registerBlock( + testMultihashCode, + testCodec, + testBlockSize, + testIDSize, + func(cid cid.Cid) (Block, error) { + return newEmptyTestBlock(cid) + }, + ) +} + +func testBlockstore(ctx context.Context, t *testing.T, items int) (blockstore.Blockstore, *cid.Set) { + bstore := blockstore.NewBlockstore(dssync.MutexWrap(ds.NewMapDatastore())) + + cids := cid.NewSet() + for i := range items { + blk := newTestBlock(i) + bitswapBlk, err := convertBitswap(blk) + require.NoError(t, err) + err = bstore.Put(ctx, bitswapBlk) + require.NoError(t, err) + cids.Add(blk.CID()) + } + return bstore, cids +} + +type testID uint16 + +func (t testID) MarshalBinary() (data []byte, err error) { + data = binary.BigEndian.AppendUint16(data, uint16(t)) + return data, nil +} + +func (t *testID) UnmarshalBinary(data []byte) error { + *t = testID(binary.BigEndian.Uint16(data)) + return nil +} + +type testBlock struct { + id testID + data []byte +} + +func newTestBlock(id int) *testBlock { + bytes := make([]byte, testBlockSize) + _, _ = crand.Read(bytes) + return &testBlock{id: testID(id), data: bytes} +} + +func newEmptyTestBlock(cid cid.Cid) (*testBlock, error) { + idData, err := extractFromCID(cid) + if err != nil { + return nil, err + } + + var id testID + err = id.UnmarshalBinary(idData) + if err != nil { + return nil, err + } + + return &testBlock{id: id}, nil +} + +func (t *testBlock) CID() cid.Cid { + return encodeToCID(t.id, testMultihashCode, testCodec) +} + +func (t *testBlock) Height() uint64 { + return 1 +} + +func (t *testBlock) Populate(context.Context, eds.Accessor) error { + return nil // noop +} + +func (t *testBlock) Marshal() ([]byte, error) { + return t.data, nil +} + +func (t *testBlock) UnmarshalFn(*share.AxisRoots) UnmarshalFn { + return func(bytes, _ []byte) error { + t.data = bytes + time.Sleep(time.Millisecond * 1) + return nil + } +} diff --git a/share/shwap/p2p/bitswap/cid.go b/share/shwap/p2p/bitswap/cid.go new file mode 100644 index 0000000000..af7f714de7 --- /dev/null +++ b/share/shwap/p2p/bitswap/cid.go @@ -0,0 +1,55 @@ +package bitswap + +import ( + "encoding" + "fmt" + + "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" +) + +// extractFromCID retrieves Shwap ID out of the CID. +func extractFromCID(cid cid.Cid) ([]byte, error) { + if err := validateCID(cid); err != nil { + return nil, fmt.Errorf("invalid cid %s: %w", cid, err) + } + // mhPrefixSize is the size of the multihash prefix that used to cut it off. + const mhPrefixSize = 4 + return cid.Hash()[mhPrefixSize:], nil +} + +// encodeToCID encodes Shwap ID into the CID. +func encodeToCID(bm encoding.BinaryMarshaler, mhcode, codec uint64) cid.Cid { + data, err := bm.MarshalBinary() + if err != nil { + panic(fmt.Errorf("marshaling for CID: %w", err)) + } + + buf, err := mh.Encode(data, mhcode) + if err != nil { + panic(fmt.Errorf("encoding to CID: %w", err)) + } + + return cid.NewCidV1(codec, buf) +} + +// validateCID checks correctness of the CID. +func validateCID(cid cid.Cid) error { + spec, err := getSpec(cid) + if err != nil { + return err + } + + prefix := cid.Prefix() + if prefix.Version != 1 { + return fmt.Errorf("invalid cid version %d", prefix.Version) + } + if prefix.MhType != spec.mhCode { + return fmt.Errorf("invalid multihash type %d", prefix.MhType) + } + if prefix.MhLength != spec.idSize { + return fmt.Errorf("invalid multihash length %d", prefix.MhLength) + } + + return nil +} diff --git a/share/shwap/p2p/bitswap/getter.go b/share/shwap/p2p/bitswap/getter.go new file mode 100644 index 0000000000..f008809833 --- /dev/null +++ b/share/shwap/p2p/bitswap/getter.go @@ -0,0 +1,353 @@ +package bitswap + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/header" + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/availability" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var tracer = otel.Tracer("shwap/bitswap") + +// Getter implements share.Getter. +type Getter struct { + exchange exchange.SessionExchange + bstore blockstore.Blockstore + availWndw time.Duration + + availablePool *pool + archivalPool *pool + + cancel context.CancelFunc +} + +// NewGetter constructs a new Getter. +func NewGetter( + exchange exchange.SessionExchange, + bstore blockstore.Blockstore, + availWndw time.Duration, +) *Getter { + return &Getter{ + exchange: exchange, + bstore: bstore, + availWndw: availWndw, + availablePool: newPool(exchange), + archivalPool: newPool(exchange), + } +} + +// Start kicks off internal fetching sessions. +// +// We keep Bitswap sessions for the whole Getter lifespan: +// - Sessions retain useful heuristics about peers, like TTFB +// - Sessions prefer peers that previously served us related content. +// +// So reusing session is expected to improve fetching performance. +// +// There are two sessions for archival and available data, so archival node peers aren't mixed +// with regular full node peers. +func (g *Getter) Start() { + ctx, cancel := context.WithCancel(context.Background()) + g.cancel = cancel + + g.availablePool.ctx = ctx + g.availablePool.ctx = ctx +} + +// Stop shuts down Getter's internal fetching getSession. +func (g *Getter) Stop() { + g.cancel() +} + +// GetSamples uses [SampleBlock] and [Fetch] to get and verify samples for given coordinates. +func (g *Getter) GetSamples( + ctx context.Context, + hdr *header.ExtendedHeader, + indices []shwap.SampleCoords, +) ([]shwap.Sample, error) { + if len(indices) == 0 { + return nil, shwap.ErrNoSampleIndicies + } + + ctx, span := tracer.Start(ctx, "get-samples", trace.WithAttributes( + attribute.Int("amount", len(indices)), + )) + defer span.End() + + blks := make([]Block, len(indices)) + for i, idx := range indices { + sid, err := NewEmptySampleBlock(hdr.Height(), idx, len(hdr.DAH.RowRoots)) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "NewEmptySampleBlock") + return nil, err + } + + blks[i] = sid + } + + isArchival := g.isArchival(hdr) + span.SetAttributes(attribute.Bool("is_archival", isArchival)) + + ses, release := g.getSession(isArchival) + defer release() + + err := Fetch(ctx, g.exchange, hdr.DAH, blks, WithStore(g.bstore), WithFetcher(ses)) + + var fetched int + smpls := make([]shwap.Sample, len(blks)) + for i, blk := range blks { + c := blk.(*SampleBlock).Container + if !c.IsEmpty() { + fetched++ + smpls[i] = c + } + } + + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "Fetch") + if fetched > 0 { + span.SetAttributes(attribute.Int("fetched", fetched)) + return smpls, err + } + return nil, err + } + + span.SetStatus(codes.Ok, "") + return smpls, nil +} + +func (g *Getter) GetRow(ctx context.Context, hdr *header.ExtendedHeader, rowIdx int) (shwap.Row, error) { + ctx, span := tracer.Start(ctx, "get-eds") + defer span.End() + + blk, err := NewEmptyRowBlock(hdr.Height(), rowIdx, len(hdr.DAH.RowRoots)) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "NewEmptyRowBlock") + return shwap.Row{}, err + } + + isArchival := g.isArchival(hdr) + span.SetAttributes(attribute.Bool("is_archival", isArchival)) + + ses, release := g.getSession(isArchival) + defer release() + + err = Fetch(ctx, g.exchange, hdr.DAH, []Block{blk}, WithFetcher(ses)) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "Fetch") + return shwap.Row{}, err + } + return blk.Container, nil +} + +// GetEDS uses [RowBlock] and [Fetch] to get half of the first EDS quadrant(ODS) and +// recomputes the whole EDS from it. +// We fetch the ODS or Q1 to ensure better compatibility with archival nodes that only +// store ODS and do not recompute other quadrants. +func (g *Getter) GetEDS( + ctx context.Context, + hdr *header.ExtendedHeader, +) (*rsmt2d.ExtendedDataSquare, error) { + ctx, span := tracer.Start(ctx, "get-eds") + defer span.End() + + sqrLn := len(hdr.DAH.RowRoots) + blks := make([]Block, sqrLn/2) + for i := 0; i < sqrLn/2; i++ { + blk, err := NewEmptyRowBlock(hdr.Height(), i, sqrLn) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "NewEmptyRowBlock") + return nil, err + } + + blks[i] = blk + } + + isArchival := g.isArchival(hdr) + span.SetAttributes(attribute.Bool("is_archival", isArchival)) + + ses, release := g.getSession(isArchival) + defer release() + + err := Fetch(ctx, g.exchange, hdr.DAH, blks, WithFetcher(ses)) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "Fetch") + return nil, err + } + + rows := make([]shwap.Row, len(blks)) + for i, blk := range blks { + rows[i] = blk.(*RowBlock).Container + } + + square, err := edsFromRows(hdr.DAH, rows) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "edsFromRows") + return nil, err + } + + span.SetStatus(codes.Ok, "") + return square, nil +} + +// GetNamespaceData uses [RowNamespaceDataBlock] and [Fetch] to get all the data +// by the given namespace. If data spans over multiple rows, the request is split into +// parallel RowNamespaceDataID requests per each row and then assembled back into NamespaceData. +func (g *Getter) GetNamespaceData( + ctx context.Context, + hdr *header.ExtendedHeader, + ns libshare.Namespace, +) (shwap.NamespaceData, error) { + if err := ns.ValidateForData(); err != nil { + return nil, err + } + + ctx, span := tracer.Start(ctx, "get-shares-by-namespace") + defer span.End() + + rowIdxs, err := share.RowsWithNamespace(hdr.DAH, ns) + if err != nil { + return nil, err + } + blks := make([]Block, len(rowIdxs)) + for i, rowNdIdx := range rowIdxs { + rndblk, err := NewEmptyRowNamespaceDataBlock(hdr.Height(), rowNdIdx, ns, len(hdr.DAH.RowRoots)) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "NewEmptyRowNamespaceDataBlock") + return nil, err + } + blks[i] = rndblk + } + + isArchival := g.isArchival(hdr) + span.SetAttributes(attribute.Bool("is_archival", isArchival)) + + ses, release := g.getSession(isArchival) + defer release() + + if err = Fetch(ctx, g.exchange, hdr.DAH, blks, WithFetcher(ses)); err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "Fetch") + return nil, err + } + + nsShrs := make(shwap.NamespaceData, len(blks)) + for i, blk := range blks { + rnd := blk.(*RowNamespaceDataBlock).Container + nsShrs[i] = shwap.RowNamespaceData{ + Shares: rnd.Shares, + Proof: rnd.Proof, + } + } + + span.SetStatus(codes.Ok, "") + return nsShrs, nil +} + +// isArchival reports whether the header is for archival data +func (g *Getter) isArchival(hdr *header.ExtendedHeader) bool { + return !availability.IsWithinWindow(hdr.Time(), g.availWndw) +} + +// getSession takes a session out of the respective session pool +func (g *Getter) getSession(isArchival bool) (ses exchange.Fetcher, release func()) { + if isArchival { + ses = g.archivalPool.get() + return ses, func() { g.archivalPool.put(ses) } + } + ses = g.availablePool.get() + return ses, func() { g.availablePool.put(ses) } +} + +// edsFromRows imports given Rows and computes EDS out of them, assuming enough Rows were provided. +// It is designed to reuse Row halves computed during verification on [Fetch] level. +func edsFromRows(roots *share.AxisRoots, rows []shwap.Row) (*rsmt2d.ExtendedDataSquare, error) { + shrs := make([]libshare.Share, len(roots.RowRoots)*len(roots.RowRoots)) + for i, row := range rows { + rowShrs, err := row.Shares() + if err != nil { + return nil, fmt.Errorf("decoding Shares out of Row: %w", err) + } + + for j, shr := range rowShrs { + shrs[j+(i*len(roots.RowRoots))] = shr + } + } + + square, err := rsmt2d.ImportExtendedDataSquare( + libshare.ToBytes(shrs), + share.DefaultRSMT2DCodec(), + wrapper.NewConstructor(uint64(len(roots.RowRoots)/2)), + ) + if err != nil { + return nil, fmt.Errorf("importing EDS: %w", err) + } + + err = square.Repair(roots.RowRoots, roots.ColumnRoots) + if err != nil { + return nil, fmt.Errorf("repairing EDS: %w", err) + } + + return square, nil +} + +// pool is a pool of Bitswap sessions. +type pool struct { + lock sync.Mutex + sessions []exchange.Fetcher + ctx context.Context + exchange exchange.SessionExchange +} + +func newPool(ex exchange.SessionExchange) *pool { + return &pool{ + exchange: ex, + sessions: make([]exchange.Fetcher, 0), + } +} + +// get returns a session from the pool or creates a new one if the pool is empty. +func (p *pool) get() exchange.Fetcher { + p.lock.Lock() + defer p.lock.Unlock() + + if len(p.sessions) == 0 { + return p.exchange.NewSession(p.ctx) + } + + ses := p.sessions[len(p.sessions)-1] + p.sessions = p.sessions[:len(p.sessions)-1] + return ses +} + +// put returns a session to the pool. +func (p *pool) put(ses exchange.Fetcher) { + p.lock.Lock() + defer p.lock.Unlock() + + p.sessions = append(p.sessions, ses) +} diff --git a/share/shwap/p2p/bitswap/getter_test.go b/share/shwap/p2p/bitswap/getter_test.go new file mode 100644 index 0000000000..b0e8d633cc --- /dev/null +++ b/share/shwap/p2p/bitswap/getter_test.go @@ -0,0 +1,116 @@ +package bitswap + +import ( + "context" + "sync" + "testing" + + "github.com/ipfs/boxo/exchange" + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestEDSFromRows(t *testing.T) { + edsIn := edstest.RandEDS(t, 8) + roots, err := share.NewAxisRoots(edsIn) + require.NoError(t, err) + + rows := make([]shwap.Row, edsIn.Width()/2) + for i := range edsIn.Width() / 2 { + rowShrs, err := libshare.FromBytes(edsIn.Row(i)[:edsIn.Width()/2]) + require.NoError(t, err) + rows[i] = shwap.NewRow(rowShrs, shwap.Left) + } + + edsOut, err := edsFromRows(roots, rows) + require.NoError(t, err) + require.True(t, edsIn.Equals(edsOut)) +} + +// mockSessionExchange is a mock implementation of exchange.SessionExchange +type mockSessionExchange struct { + exchange.SessionExchange + sessionCount int + mu sync.Mutex +} + +func (m *mockSessionExchange) NewSession(ctx context.Context) exchange.Fetcher { + m.mu.Lock() + defer m.mu.Unlock() + m.sessionCount++ + return &mockFetcher{id: m.sessionCount} +} + +// mockFetcher is a mock implementation of exchange.Fetcher +type mockFetcher struct { + exchange.Fetcher + id int +} + +func TestPoolGetFromEmptyPool(t *testing.T) { + ex := &mockSessionExchange{} + p := newPool(ex) + ctx := context.Background() + p.ctx = ctx + + ses := p.get().(*mockFetcher) + require.NotNil(t, ses) + require.Equal(t, 1, ses.id) +} + +func TestPoolPutAndGet(t *testing.T) { + ex := &mockSessionExchange{} + p := newPool(ex) + ctx := context.Background() + p.ctx = ctx + + // Get a session + ses := p.get().(*mockFetcher) + + // Put it back + p.put(ses) + + // Get again + ses2 := p.get().(*mockFetcher) + + require.Equal(t, ses.id, ses2.id) +} + +func TestPoolConcurrency(t *testing.T) { + ex := &mockSessionExchange{} + p := newPool(ex) + ctx := context.Background() + p.ctx = ctx + + const numGoroutines = 50 + var wg sync.WaitGroup + + sessionIDSet := make(map[int]struct{}) + lock := sync.Mutex{} + + // Start multiple goroutines to get sessions + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func() { + defer wg.Done() + ses := p.get() + mockSes := ses.(*mockFetcher) + p.put(ses) + lock.Lock() + sessionIDSet[mockSes.id] = struct{}{} + lock.Unlock() + }() + } + wg.Wait() + + // Since the pool reuses sessions, the number of unique session IDs should be less than or equal to numGoroutines + if len(sessionIDSet) > numGoroutines { + t.Fatalf("expected number of unique sessions to be less than or equal to %d, got %d", + numGoroutines, len(sessionIDSet)) + } +} diff --git a/share/shwap/p2p/bitswap/pb/bitswap.pb.go b/share/shwap/p2p/bitswap/pb/bitswap.pb.go new file mode 100644 index 0000000000..a84077e9ef --- /dev/null +++ b/share/shwap/p2p/bitswap/pb/bitswap.pb.go @@ -0,0 +1,372 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: share/shwap/p2p/bitswap/pb/bitswap.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Block struct { + Cid []byte `protobuf:"bytes,1,opt,name=cid,proto3" json:"cid,omitempty"` + Container []byte `protobuf:"bytes,2,opt,name=container,proto3" json:"container,omitempty"` +} + +func (m *Block) Reset() { *m = Block{} } +func (m *Block) String() string { return proto.CompactTextString(m) } +func (*Block) ProtoMessage() {} +func (*Block) Descriptor() ([]byte, []int) { + return fileDescriptor_09fd4e2ff1d5ce94, []int{0} +} +func (m *Block) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Block) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Block.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Block) XXX_Merge(src proto.Message) { + xxx_messageInfo_Block.Merge(m, src) +} +func (m *Block) XXX_Size() int { + return m.Size() +} +func (m *Block) XXX_DiscardUnknown() { + xxx_messageInfo_Block.DiscardUnknown(m) +} + +var xxx_messageInfo_Block proto.InternalMessageInfo + +func (m *Block) GetCid() []byte { + if m != nil { + return m.Cid + } + return nil +} + +func (m *Block) GetContainer() []byte { + if m != nil { + return m.Container + } + return nil +} + +func init() { + proto.RegisterType((*Block)(nil), "bitswap.Block") +} + +func init() { + proto.RegisterFile("share/shwap/p2p/bitswap/pb/bitswap.proto", fileDescriptor_09fd4e2ff1d5ce94) +} + +var fileDescriptor_09fd4e2ff1d5ce94 = []byte{ + // 171 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x28, 0xce, 0x48, 0x2c, + 0x4a, 0xd5, 0x2f, 0xce, 0x28, 0x4f, 0x2c, 0xd0, 0x2f, 0x30, 0x2a, 0xd0, 0x4f, 0xca, 0x2c, 0x29, + 0x06, 0xb3, 0x93, 0x60, 0x4c, 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0x76, 0x28, 0x57, 0xc9, + 0x9c, 0x8b, 0xd5, 0x29, 0x27, 0x3f, 0x39, 0x5b, 0x48, 0x80, 0x8b, 0x39, 0x39, 0x33, 0x45, 0x82, + 0x51, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc4, 0x14, 0x92, 0xe1, 0xe2, 0x4c, 0xce, 0xcf, 0x2b, 0x49, + 0xcc, 0xcc, 0x4b, 0x2d, 0x92, 0x60, 0x02, 0x8b, 0x23, 0x04, 0x9c, 0x22, 0x4f, 0x3c, 0x92, 0x63, + 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, + 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x3e, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, + 0x3f, 0x57, 0x3f, 0x39, 0x35, 0x27, 0xb5, 0xb8, 0x24, 0x33, 0x31, 0xbf, 0x28, 0x1d, 0xce, 0xd6, + 0xcd, 0xcb, 0x4f, 0x01, 0x39, 0x12, 0x97, 0x53, 0x93, 0xd8, 0xc0, 0x6e, 0x34, 0x06, 0x04, 0x00, + 0x00, 0xff, 0xff, 0xe7, 0x9c, 0x32, 0xc5, 0xcf, 0x00, 0x00, 0x00, +} + +func (m *Block) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Block) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Block) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Container) > 0 { + i -= len(m.Container) + copy(dAtA[i:], m.Container) + i = encodeVarintBitswap(dAtA, i, uint64(len(m.Container))) + i-- + dAtA[i] = 0x12 + } + if len(m.Cid) > 0 { + i -= len(m.Cid) + copy(dAtA[i:], m.Cid) + i = encodeVarintBitswap(dAtA, i, uint64(len(m.Cid))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintBitswap(dAtA []byte, offset int, v uint64) int { + offset -= sovBitswap(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Block) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Cid) + if l > 0 { + n += 1 + l + sovBitswap(uint64(l)) + } + l = len(m.Container) + if l > 0 { + n += 1 + l + sovBitswap(uint64(l)) + } + return n +} + +func sovBitswap(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozBitswap(x uint64) (n int) { + return sovBitswap(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Block) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBitswap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Block: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Block: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cid", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBitswap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBitswap + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBitswap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cid = append(m.Cid[:0], dAtA[iNdEx:postIndex]...) + if m.Cid == nil { + m.Cid = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Container", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBitswap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthBitswap + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthBitswap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Container = append(m.Container[:0], dAtA[iNdEx:postIndex]...) + if m.Container == nil { + m.Container = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBitswap(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBitswap + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipBitswap(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowBitswap + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowBitswap + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowBitswap + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthBitswap + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupBitswap + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthBitswap + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthBitswap = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowBitswap = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupBitswap = fmt.Errorf("proto: unexpected end of group") +) diff --git a/share/shwap/p2p/bitswap/pb/bitswap.proto b/share/shwap/p2p/bitswap/pb/bitswap.proto new file mode 100644 index 0000000000..3ba19aa49c --- /dev/null +++ b/share/shwap/p2p/bitswap/pb/bitswap.proto @@ -0,0 +1,9 @@ +// Defined in CIP-19 https://github.com/celestiaorg/CIPs/blob/82aeb7dfc472105a11babffd548c730c899a3d24/cips/cip-19.md +syntax = "proto3"; +package bitswap; +option go_package = "github.com/celestiaorg/celestia-node/share/shwap/p2p/bitswap/pb"; + +message Block { + bytes cid = 1; + bytes container = 2; +} diff --git a/share/shwap/p2p/bitswap/row_block.go b/share/shwap/p2p/bitswap/row_block.go new file mode 100644 index 0000000000..4bf1966d8c --- /dev/null +++ b/share/shwap/p2p/bitswap/row_block.go @@ -0,0 +1,137 @@ +package bitswap + +import ( + "context" + "fmt" + + "github.com/ipfs/go-cid" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" + shwappb "github.com/celestiaorg/celestia-node/share/shwap/pb" +) + +const ( + // rowCodec is a CID codec used for row Bitswap requests over Namespaced Merkle Tree. + rowCodec = 0x7800 + + // rowMultihashCode is the multihash code for custom axis sampling multihash function. + rowMultihashCode = 0x7801 +) + +// maxRowSize is the maximum size of the RowBlock. +// It is calculated as half of the square size multiplied by the share size. +var maxRowSize = share.MaxSquareSize / 2 * libshare.ShareSize + +func init() { + registerBlock( + rowMultihashCode, + rowCodec, + maxRowSize, + shwap.RowIDSize, + func(cid cid.Cid) (Block, error) { + return EmptyRowBlockFromCID(cid) + }, + ) +} + +// RowBlock is a Bitswap compatible block for Shwap's Row container. +type RowBlock struct { + ID shwap.RowID + + Container shwap.Row +} + +// NewEmptyRowBlock constructs a new empty RowBlock. +func NewEmptyRowBlock(height uint64, rowIdx, edsSize int) (*RowBlock, error) { + id, err := shwap.NewRowID(height, rowIdx, edsSize) + if err != nil { + return nil, err + } + + return &RowBlock{ID: id}, nil +} + +// EmptyRowBlockFromCID constructs an empty RowBlock out of the CID. +func EmptyRowBlockFromCID(cid cid.Cid) (*RowBlock, error) { + ridData, err := extractFromCID(cid) + if err != nil { + return nil, err + } + + rid, err := shwap.RowIDFromBinary(ridData) + if err != nil { + return nil, fmt.Errorf("while unmarhaling RowBlock: %w", err) + } + return &RowBlock{ID: rid}, nil +} + +func (rb *RowBlock) CID() cid.Cid { + return encodeToCID(rb.ID, rowMultihashCode, rowCodec) +} + +func (rb *RowBlock) Height() uint64 { + return rb.ID.Height +} + +func (rb *RowBlock) Marshal() ([]byte, error) { + if rb.Container.IsEmpty() { + return nil, fmt.Errorf("cannot marshal empty RowBlock") + } + + container := rb.Container.ToProto() + containerData, err := container.Marshal() + if err != nil { + return nil, fmt.Errorf("marshaling RowBlock container: %w", err) + } + + return containerData, nil +} + +func (rb *RowBlock) Populate(ctx context.Context, eds eds.Accessor) error { + half, err := eds.AxisHalf(ctx, rsmt2d.Row, rb.ID.RowIndex) + if err != nil { + return fmt.Errorf("accessing Row AxisHalf: %w", err) + } + + rb.Container = half.ToRow() + return nil +} + +func (rb *RowBlock) UnmarshalFn(root *share.AxisRoots) UnmarshalFn { + return func(cntrData, idData []byte) error { + if !rb.Container.IsEmpty() { + return nil + } + + rid, err := shwap.RowIDFromBinary(idData) + if err != nil { + return fmt.Errorf("unmarhaling RowID: %w", err) + } + + if !rb.ID.Equals(rid) { + return fmt.Errorf("requested %+v doesnt match given %+v", rb.ID, rid) + } + + var row shwappb.Row + if err := row.Unmarshal(cntrData); err != nil { + return fmt.Errorf("unmarshaling Row for %+v: %w", rb.ID, err) + } + + cntr, err := shwap.RowFromProto(&row) + if err != nil { + return fmt.Errorf("unmarshaling Row: %w", err) + } + + if err = cntr.Verify(root, rb.ID.RowIndex); err != nil { + return fmt.Errorf("validating Row for %+v: %w", rb.ID, err) + } + + rb.Container = cntr + return nil + } +} diff --git a/share/shwap/p2p/bitswap/row_block_test.go b/share/shwap/p2p/bitswap/row_block_test.go new file mode 100644 index 0000000000..f609683da5 --- /dev/null +++ b/share/shwap/p2p/bitswap/row_block_test.go @@ -0,0 +1,38 @@ +package bitswap + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" +) + +func TestRow_FetchRoundtrip(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + eds := edstest.RandEDS(t, 4) + root, err := share.NewAxisRoots(eds) + require.NoError(t, err) + exchange := newExchangeOverEDS(ctx, t, eds) + + blks := make([]Block, eds.Width()) + for i := range blks { + blk, err := NewEmptyRowBlock(1, i, len(root.RowRoots)) + require.NoError(t, err) + blks[i] = blk + } + + err = Fetch(ctx, exchange, root, blks) + require.NoError(t, err) + + for _, blk := range blks { + row := blk.(*RowBlock) + err = row.Container.Verify(root, row.ID.RowIndex) + require.NoError(t, err) + } +} diff --git a/share/shwap/p2p/bitswap/row_namespace_data_block.go b/share/shwap/p2p/bitswap/row_namespace_data_block.go new file mode 100644 index 0000000000..1f9d26907c --- /dev/null +++ b/share/shwap/p2p/bitswap/row_namespace_data_block.go @@ -0,0 +1,140 @@ +package bitswap + +import ( + "context" + "fmt" + + "github.com/ipfs/go-cid" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" + shwappb "github.com/celestiaorg/celestia-node/share/shwap/pb" +) + +const ( + // rowNamespaceDataCodec is a CID codec used for data Bitswap requests over Namespaced Merkle Tree. + rowNamespaceDataCodec = 0x7820 + + // rowNamespaceDataMultihashCode is the multihash code for data multihash function. + rowNamespaceDataMultihashCode = 0x7821 +) + +// maxRNDSize is the maximum size of the RowNamespaceDataBlock. +var maxRNDSize = maxRowSize + +func init() { + registerBlock( + rowNamespaceDataMultihashCode, + rowNamespaceDataCodec, + maxRNDSize, + shwap.RowNamespaceDataIDSize, + func(cid cid.Cid) (Block, error) { + return EmptyRowNamespaceDataBlockFromCID(cid) + }, + ) +} + +// RowNamespaceDataBlock is a Bitswap compatible block for Shwap's RowNamespaceData container. +type RowNamespaceDataBlock struct { + ID shwap.RowNamespaceDataID + + Container shwap.RowNamespaceData +} + +// NewEmptyRowNamespaceDataBlock constructs a new empty RowNamespaceDataBlock. +func NewEmptyRowNamespaceDataBlock( + height uint64, + rowIdx int, + namespace libshare.Namespace, + edsSize int, +) (*RowNamespaceDataBlock, error) { + id, err := shwap.NewRowNamespaceDataID(height, rowIdx, namespace, edsSize) + if err != nil { + return nil, err + } + + return &RowNamespaceDataBlock{ID: id}, nil +} + +// EmptyRowNamespaceDataBlockFromCID constructs an empty RowNamespaceDataBlock out of the CID. +func EmptyRowNamespaceDataBlockFromCID(cid cid.Cid) (*RowNamespaceDataBlock, error) { + rndidData, err := extractFromCID(cid) + if err != nil { + return nil, err + } + + rndid, err := shwap.RowNamespaceDataIDFromBinary(rndidData) + if err != nil { + return nil, fmt.Errorf("unmarhalling RowNamespaceDataBlock: %w", err) + } + + return &RowNamespaceDataBlock{ID: rndid}, nil +} + +func (rndb *RowNamespaceDataBlock) CID() cid.Cid { + return encodeToCID(rndb.ID, rowNamespaceDataMultihashCode, rowNamespaceDataCodec) +} + +func (rndb *RowNamespaceDataBlock) Height() uint64 { + return rndb.ID.Height +} + +func (rndb *RowNamespaceDataBlock) Marshal() ([]byte, error) { + if rndb.Container.IsEmpty() { + return nil, fmt.Errorf("cannot marshal empty RowNamespaceDataBlock") + } + + container := rndb.Container.ToProto() + containerData, err := container.Marshal() + if err != nil { + return nil, fmt.Errorf("marshaling RowNamespaceDataBlock container: %w", err) + } + + return containerData, nil +} + +func (rndb *RowNamespaceDataBlock) Populate(ctx context.Context, eds eds.Accessor) error { + rnd, err := eds.RowNamespaceData(ctx, rndb.ID.DataNamespace, rndb.ID.RowIndex) + if err != nil { + return fmt.Errorf("accessing RowNamespaceData: %w", err) + } + + rndb.Container = rnd + return nil +} + +func (rndb *RowNamespaceDataBlock) UnmarshalFn(root *share.AxisRoots) UnmarshalFn { + return func(cntrData, idData []byte) error { + if !rndb.Container.IsEmpty() { + return nil + } + + rndid, err := shwap.RowNamespaceDataIDFromBinary(idData) + if err != nil { + return fmt.Errorf("unmarhaling RowNamespaceDataID: %w", err) + } + + if !rndb.ID.Equals(rndid) { + return fmt.Errorf("requested %+v doesnt match given %+v", rndb.ID, rndid) + } + + var rnd shwappb.RowNamespaceData + if err := rnd.Unmarshal(cntrData); err != nil { + return fmt.Errorf("unmarshaling RowNamespaceData for %+v: %w", rndb.ID, err) + } + + cntr, err := shwap.RowNamespaceDataFromProto(&rnd) + if err != nil { + return fmt.Errorf("unmarshaling RowNamespaceData for %+v: %w", rndb.ID, err) + } + if err := cntr.Verify(root, rndb.ID.DataNamespace, rndb.ID.RowIndex); err != nil { + return fmt.Errorf("validating RowNamespaceData for %+v: %w", rndb.ID, err) + } + + rndb.Container = cntr + return nil + } +} diff --git a/share/shwap/p2p/bitswap/row_namespace_data_block_test.go b/share/shwap/p2p/bitswap/row_namespace_data_block_test.go new file mode 100644 index 0000000000..c4cd00999c --- /dev/null +++ b/share/shwap/p2p/bitswap/row_namespace_data_block_test.go @@ -0,0 +1,41 @@ +package bitswap + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" +) + +func TestRowNamespaceData_FetchRoundtrip(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + namespace := libshare.RandomNamespace() + eds, root := edstest.RandEDSWithNamespace(t, namespace, 64, 16) + exchange := newExchangeOverEDS(ctx, t, eds) + + rowIdxs, err := share.RowsWithNamespace(root, namespace) + require.NoError(t, err) + blks := make([]Block, len(rowIdxs)) + for i, rowIdx := range rowIdxs { + blk, err := NewEmptyRowNamespaceDataBlock(1, rowIdx, namespace, len(root.RowRoots)) + require.NoError(t, err) + blks[i] = blk + } + + err = Fetch(ctx, exchange, root, blks) + require.NoError(t, err) + + for _, blk := range blks { + rnd := blk.(*RowNamespaceDataBlock) + err = rnd.Container.Verify(root, rnd.ID.DataNamespace, rnd.ID.RowIndex) + require.NoError(t, err) + } +} diff --git a/share/shwap/p2p/bitswap/sample_block.go b/share/shwap/p2p/bitswap/sample_block.go new file mode 100644 index 0000000000..8d22047330 --- /dev/null +++ b/share/shwap/p2p/bitswap/sample_block.go @@ -0,0 +1,140 @@ +package bitswap + +import ( + "context" + "fmt" + "math" + + "github.com/ipfs/go-cid" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" + shwappb "github.com/celestiaorg/celestia-node/share/shwap/pb" +) + +const ( + // sampleCodec is a CID codec used for share sampling Bitswap requests over Namespaced + // Merkle Tree. + sampleCodec = 0x7810 + + // sampleMultihashCode is the multihash code for share sampling multihash function. + sampleMultihashCode = 0x7811 +) + +// maxSampleSize is the maximum size of the SampleBlock. +// It is calculated as the size of the share plus the size of the proof. +var maxSampleSize = libshare.ShareSize + share.AxisRootSize*int(math.Log2(float64(share.MaxSquareSize))) + +func init() { + registerBlock( + sampleMultihashCode, + sampleCodec, + maxSampleSize, + shwap.SampleIDSize, + func(cid cid.Cid) (Block, error) { + return EmptySampleBlockFromCID(cid) + }, + ) +} + +// SampleBlock is a Bitswap compatible block for Shwap's Sample container. +type SampleBlock struct { + ID shwap.SampleID + Container shwap.Sample +} + +// NewEmptySampleBlock constructs a new empty SampleBlock. +func NewEmptySampleBlock(height uint64, idx shwap.SampleCoords, edsSize int) (*SampleBlock, error) { + id, err := shwap.NewSampleID(height, idx, edsSize) + if err != nil { + return nil, err + } + + return &SampleBlock{ID: id}, nil +} + +// EmptySampleBlockFromCID constructs an empty SampleBlock out of the CID. +func EmptySampleBlockFromCID(cid cid.Cid) (*SampleBlock, error) { + sidData, err := extractFromCID(cid) + if err != nil { + return nil, err + } + + sid, err := shwap.SampleIDFromBinary(sidData) + if err != nil { + return nil, fmt.Errorf("while unmarhaling SampleBlock: %w", err) + } + + return &SampleBlock{ID: sid}, nil +} + +func (sb *SampleBlock) CID() cid.Cid { + return encodeToCID(sb.ID, sampleMultihashCode, sampleCodec) +} + +func (sb *SampleBlock) Height() uint64 { + return sb.ID.Height +} + +func (sb *SampleBlock) Marshal() ([]byte, error) { + if sb.Container.IsEmpty() { + return nil, fmt.Errorf("cannot marshal empty SampleBlock") + } + + container := sb.Container.ToProto() + containerData, err := container.Marshal() + if err != nil { + return nil, fmt.Errorf("marshaling SampleBlock container: %w", err) + } + + return containerData, nil +} + +func (sb *SampleBlock) Populate(ctx context.Context, eds eds.Accessor) error { + idx := shwap.SampleCoords{Row: sb.ID.RowIndex, Col: sb.ID.ShareIndex} + + smpl, err := eds.Sample(ctx, idx) + if err != nil { + return fmt.Errorf("accessing Sample: %w", err) + } + + sb.Container = smpl + return nil +} + +func (sb *SampleBlock) UnmarshalFn(root *share.AxisRoots) UnmarshalFn { + return func(cntrData, idData []byte) error { + if !sb.Container.IsEmpty() { + return nil + } + + sid, err := shwap.SampleIDFromBinary(idData) + if err != nil { + return fmt.Errorf("unmarhaling SampleID: %w", err) + } + + if !sb.ID.Equals(sid) { + return fmt.Errorf("requested %+v doesnt match given %+v", sb.ID, sid) + } + + var sample shwappb.Sample + if err := sample.Unmarshal(cntrData); err != nil { + return fmt.Errorf("unmarshaling Sample for %+v: %w", sb.ID, err) + } + + cntr, err := shwap.SampleFromProto(&sample) + if err != nil { + return fmt.Errorf("unmarshaling Sample for %+v: %w", sb.ID, err) + } + + if err = cntr.Verify(root, sb.ID.RowIndex, sb.ID.ShareIndex); err != nil { + return fmt.Errorf("validating Sample for %+v: %w", sb.ID, err) + } + + sb.Container = cntr + return nil + } +} diff --git a/share/shwap/p2p/bitswap/sample_block_test.go b/share/shwap/p2p/bitswap/sample_block_test.go new file mode 100644 index 0000000000..3339f24314 --- /dev/null +++ b/share/shwap/p2p/bitswap/sample_block_test.go @@ -0,0 +1,44 @@ +package bitswap + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestSample_FetchRoundtrip(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + eds := edstest.RandEDS(t, 32) + root, err := share.NewAxisRoots(eds) + require.NoError(t, err) + exchange := newExchangeOverEDS(ctx, t, eds) + + width := int(eds.Width()) + blks := make([]Block, 0, width*width) + for x := 0; x < width; x++ { + for y := 0; y < width; y++ { + idx := shwap.SampleCoords{Row: x, Col: y} + + blk, err := NewEmptySampleBlock(1, idx, len(root.RowRoots)) + require.NoError(t, err) + blks = append(blks, blk) + } + } + + err = Fetch(ctx, exchange, root, blks) + require.NoError(t, err) + + for _, sample := range blks { + blk := sample.(*SampleBlock) + err = blk.Container.Verify(root, blk.ID.RowIndex, blk.ID.ShareIndex) + require.NoError(t, err) + } +} diff --git a/share/p2p/discovery/backoff.go b/share/shwap/p2p/discovery/backoff.go similarity index 100% rename from share/p2p/discovery/backoff.go rename to share/shwap/p2p/discovery/backoff.go diff --git a/share/p2p/discovery/backoff_test.go b/share/shwap/p2p/discovery/backoff_test.go similarity index 100% rename from share/p2p/discovery/backoff_test.go rename to share/shwap/p2p/discovery/backoff_test.go diff --git a/share/shwap/p2p/discovery/dht.go b/share/shwap/p2p/discovery/dht.go new file mode 100644 index 0000000000..b3f9351c7c --- /dev/null +++ b/share/shwap/p2p/discovery/dht.go @@ -0,0 +1,38 @@ +package discovery + +import ( + "context" + "fmt" + "time" + + "github.com/ipfs/go-datastore" + dht "github.com/libp2p/go-libp2p-kad-dht" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" +) + +const ( + defaultRoutingRefreshPeriod = time.Minute +) + +// PeerRouting provides constructor for PeerRouting over DHT. +// Basically, this provides a way to discover peer addresses by respecting public keys. +func NewDHT( + ctx context.Context, + prefix string, + bootsrappers []peer.AddrInfo, + host host.Host, + dataStore datastore.Batching, + mode dht.ModeOpt, +) (*dht.IpfsDHT, error) { + opts := []dht.Option{ + dht.BootstrapPeers(bootsrappers...), + dht.ProtocolPrefix(protocol.ID(fmt.Sprintf("/celestia/%s", prefix))), + dht.Datastore(dataStore), + dht.RoutingTableRefreshPeriod(defaultRoutingRefreshPeriod), + dht.Mode(mode), + } + + return dht.New(ctx, host, opts...) +} diff --git a/share/p2p/discovery/discovery.go b/share/shwap/p2p/discovery/discovery.go similarity index 87% rename from share/p2p/discovery/discovery.go rename to share/shwap/p2p/discovery/discovery.go index fe99815d94..7736cbe3b6 100644 --- a/share/p2p/discovery/discovery.go +++ b/share/shwap/p2p/discovery/discovery.go @@ -42,8 +42,11 @@ var discoveryRetryTimeout = retryTimeout // Discovery combines advertise and discover services and allows to store discovered nodes. // TODO: The code here gets horribly hairy, so we should refactor this at some point type Discovery struct { - // Tag is used as rendezvous point for discovery service - tag string + // tag is used to specify topic for discovery + tag string + // topic contains tag and current version of discovery protocol and is used as + // rendezvous point for discovery service + topic string set *limitedSet host host.Host disc discovery.Discovery @@ -74,11 +77,15 @@ func (f OnUpdatedPeers) add(next OnUpdatedPeers) OnUpdatedPeers { } // NewDiscovery constructs a new discovery. +// It accepts tag which is a rendezvous point/topic for peers +// to advertise and discover each other. Tag suffix is used to create +// subnetworks within a tag, e.g. for different protocol versions yet running +// over the same p2p network. func NewDiscovery( params *Parameters, h host.Host, d discovery.Discovery, - tag string, + tag, tagSuffix string, opts ...Option, ) (*Discovery, error) { if err := params.Validate(); err != nil { @@ -91,6 +98,7 @@ func NewDiscovery( o := newOptions(opts...) return &Discovery{ tag: tag, + topic: fmt.Sprintf("/%s/%s", tag, tagSuffix), set: newLimitedSet(params.PeersLimit), host: h, disc: d, @@ -116,11 +124,11 @@ func (d *Discovery) Start(context.Context) error { go d.connector.GC(ctx) if d.advertise { - log.Infow("advertising to topic", "topic", d.tag) + log.Infow("advertising to topic", "topic", d.topic) go d.Advertise(ctx) } - log.Infow("started discovery", "topic", d.tag) + log.Infow("started discovery", "topic", d.topic) return nil } @@ -147,7 +155,7 @@ func (d *Discovery) Discard(id peer.ID) bool { return false } - d.host.ConnManager().Unprotect(id, d.tag) + d.host.ConnManager().Unprotect(id, d.topic) d.connector.Backoff(id) d.set.Remove(id) d.onUpdatedPeers(id, false) @@ -170,14 +178,14 @@ func (d *Discovery) Advertise(ctx context.Context) { timer := time.NewTimer(d.params.AdvertiseInterval) defer timer.Stop() for { - log.Infof("advertising to topic %s", d.tag) - _, err := d.disc.Advertise(ctx, d.tag) + log.Infof("advertising to topic %s", d.topic) + _, err := d.disc.Advertise(ctx, d.topic) d.metrics.observeAdvertise(ctx, err) if err != nil { if ctx.Err() != nil { return } - log.Warnw("error advertising", "rendezvous", d.tag, "err", err) + log.Warnw("error advertising", "rendezvous", d.topic, "err", err) // we don't want retry indefinitely in busy loop // internal discovery mechanism may need some time before attempts @@ -195,7 +203,7 @@ func (d *Discovery) Advertise(ctx context.Context) { } } - log.Infof("successfully advertised to topic %s", d.tag) + log.Infof("successfully advertised to topic %s", d.topic) if !timer.Stop() { <-timer.C } @@ -232,7 +240,7 @@ func (d *Discovery) discoveryLoop(ctx context.Context) { log.Warnf( "Potentially degraded connectivity, unable to discover the desired amount of %s peers in %v. "+ "Number of peers discovered: %d. Required: %d.", - d.tag, logInterval, d.set.Size(), d.set.Limit(), + d.topic, logInterval, d.set.Size(), d.set.Limit(), ) } // Do not break the loop; just continue @@ -270,13 +278,13 @@ func (d *Discovery) discover(ctx context.Context) bool { size := d.set.Size() want := d.set.Limit() - size if want == 0 { - log.Debugw("reached soft peer limit, skipping discovery", "topic", d.tag, "size", size) + log.Debugw("reached soft peer limit, skipping discovery", "topic", d.topic, "size", size) return true } // TODO @renaynay: eventually, have a mechanism to catch if wanted amount of peers // has not been discovered in X amount of time so that users are warned of degraded // FN connectivity. - log.Debugw("discovering peers", "topic", d.tag, "want", want) + log.Debugw("discovering peers", "topic", d.topic, "want", want) // we use errgroup as it provide limits var wg errgroup.Group @@ -290,9 +298,9 @@ func (d *Discovery) discover(ctx context.Context) bool { findCancel() }() - peers, err := d.disc.FindPeers(findCtx, d.tag) + peers, err := d.disc.FindPeers(findCtx, d.topic) if err != nil { - log.Error("unable to start discovery", "topic", d.tag, "err", err) + log.Error("unable to start discovery", "topic", d.topic, "err", err) return false } @@ -317,12 +325,12 @@ func (d *Discovery) discover(ctx context.Context) bool { } size := d.set.Size() - log.Debugw("found peer", "topic", d.tag, "peer", peer.ID.String(), "found_amount", size) + log.Debugw("found peer", "topic", d.topic, "peer", peer.ID.String(), "found_amount", size) if size < d.set.Limit() { return nil } - log.Infow("discovered wanted peers", "topic", d.tag, "amount", size) + log.Infow("discovered wanted peers", "topic", d.topic, "amount", size) findCancel() // stop discovery when we are done return nil }) @@ -333,7 +341,7 @@ func (d *Discovery) discover(ctx context.Context) bool { isEnoughPeers := d.set.Size() >= d.set.Limit() d.metrics.observeFindPeers(ctx, isEnoughPeers) - log.Debugw("discovery finished", "topic", d.tag, "discovered_wanted", isEnoughPeers) + log.Debugw("discovery finished", "topic", d.topic, "discovered_wanted", isEnoughPeers) return isEnoughPeers } } @@ -385,7 +393,7 @@ func (d *Discovery) handleDiscoveredPeer(ctx context.Context, peer peer.AddrInfo // NOTE: This is does not protect from remote killing the connection. // In the future, we should design a protocol that keeps bidirectional agreement on whether // connection should be kept or not, similar to mesh link in GossipSub. - d.host.ConnManager().Protect(peer.ID, d.tag) + d.host.ConnManager().Protect(peer.ID, d.topic) return true } diff --git a/share/p2p/discovery/discovery_test.go b/share/shwap/p2p/discovery/discovery_test.go similarity index 98% rename from share/p2p/discovery/discovery_test.go rename to share/shwap/p2p/discovery/discovery_test.go index 8fb31de922..f900d38557 100644 --- a/share/p2p/discovery/discovery_test.go +++ b/share/shwap/p2p/discovery/discovery_test.go @@ -21,6 +21,7 @@ import ( const ( fullNodesTag = "full" + version = "v1" ) func TestDiscovery(t *testing.T) { @@ -168,7 +169,7 @@ func (t *testnet) startNewDiscovery( tag string, opts ...Option, ) *Discovery { - disc, err := NewDiscovery(params, hst, routingDisc, tag, opts...) + disc, err := NewDiscovery(params, hst, routingDisc, version, tag, opts...) require.NoError(t.T, err) err = disc.Start(t.ctx) require.NoError(t.T, err) diff --git a/share/p2p/discovery/metrics.go b/share/shwap/p2p/discovery/metrics.go similarity index 100% rename from share/p2p/discovery/metrics.go rename to share/shwap/p2p/discovery/metrics.go diff --git a/share/p2p/discovery/options.go b/share/shwap/p2p/discovery/options.go similarity index 100% rename from share/p2p/discovery/options.go rename to share/shwap/p2p/discovery/options.go diff --git a/share/p2p/discovery/set.go b/share/shwap/p2p/discovery/set.go similarity index 100% rename from share/p2p/discovery/set.go rename to share/shwap/p2p/discovery/set.go diff --git a/share/p2p/discovery/set_test.go b/share/shwap/p2p/discovery/set_test.go similarity index 100% rename from share/p2p/discovery/set_test.go rename to share/shwap/p2p/discovery/set_test.go diff --git a/share/p2p/doc.go b/share/shwap/p2p/shrex/doc.go similarity index 88% rename from share/p2p/doc.go rename to share/shwap/p2p/shrex/doc.go index 991ddf94db..9654532842 100644 --- a/share/p2p/doc.go +++ b/share/shwap/p2p/shrex/doc.go @@ -1,4 +1,4 @@ -// Package p2p provides p2p functionality that powers the share exchange protocols used by celestia-node. +// Package shrex provides functionality that powers the share exchange protocols used by celestia-node. // The available protocols are: // // - shrexsub : a floodsub-based pubsub protocol that is used to broadcast/subscribe to the event @@ -15,4 +15,4 @@ // and is primarily used by `getters.ShrexGetter` in share/getters/shrex.go. // // Find out more about each protocol in their respective sub-packages. -package p2p +package shrex diff --git a/share/shwap/p2p/shrex/error.go b/share/shwap/p2p/shrex/error.go new file mode 100644 index 0000000000..b5e6dce677 --- /dev/null +++ b/share/shwap/p2p/shrex/error.go @@ -0,0 +1,18 @@ +package shrex + +import ( + "errors" +) + +// ErrorContains reports whether any error in err's tree matches any error in targets tree. +func ErrorContains(err, target error) bool { + if errors.Is(err, target) || target == nil { + return true + } + + target = errors.Unwrap(target) + if target == nil { + return false + } + return ErrorContains(err, target) +} diff --git a/share/shwap/p2p/shrex/error_test.go b/share/shwap/p2p/shrex/error_test.go new file mode 100644 index 0000000000..d9b343931c --- /dev/null +++ b/share/shwap/p2p/shrex/error_test.go @@ -0,0 +1,112 @@ +package shrex + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_ErrorContains(t *testing.T) { + err1 := errors.New("1") + err2 := errors.New("2") + + w1 := func(err error) error { + return fmt.Errorf("wrap1: %w", err) + } + w2 := func(err error) error { + return fmt.Errorf("wrap1: %w", err) + } + + type args struct { + err error + target error + } + tests := []struct { + name string + args args + want bool + }{ + { + "nil err", + args{ + err: nil, + target: err1, + }, + false, + }, + { + "nil target", + args{ + err: err1, + target: nil, + }, + true, + }, + { + "errors.Is true", + args{ + err: w1(err1), + target: err1, + }, + true, + }, + { + "errors.Is false", + args{ + err: w1(err1), + target: err2, + }, + false, + }, + { + "same wrap but different base error", + args{ + err: w1(err1), + target: w1(err2), + }, + false, + }, + { + "both wrapped true", + args{ + err: w1(err1), + target: w2(err1), + }, + true, + }, + { + "both wrapped false", + args{ + err: w1(err1), + target: w2(err2), + }, + false, + }, + { + "multierr first in slice", + args{ + err: errors.Join(w1(err1), w2(err2)), + target: w2(err1), + }, + true, + }, + { + "multierr second in slice", + args{ + err: errors.Join(w1(err1), w2(err2)), + target: w1(err2), + }, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, + tt.want, + ErrorContains(tt.args.err, tt.args.target), + "ErrorContains(%v, %v)", tt.args.err, tt.args.target) + }) + } +} diff --git a/share/p2p/errors.go b/share/shwap/p2p/shrex/errors.go similarity index 98% rename from share/p2p/errors.go rename to share/shwap/p2p/shrex/errors.go index cb7b596f47..79ff0ed2b2 100644 --- a/share/p2p/errors.go +++ b/share/shwap/p2p/shrex/errors.go @@ -1,4 +1,4 @@ -package p2p +package shrex import ( "errors" diff --git a/share/p2p/metrics.go b/share/shwap/p2p/shrex/metrics.go similarity index 99% rename from share/p2p/metrics.go rename to share/shwap/p2p/shrex/metrics.go index 55aefda81d..9d5c605139 100644 --- a/share/p2p/metrics.go +++ b/share/shwap/p2p/shrex/metrics.go @@ -1,4 +1,4 @@ -package p2p +package shrex import ( "context" diff --git a/share/p2p/middleware.go b/share/shwap/p2p/shrex/middleware.go similarity index 98% rename from share/p2p/middleware.go rename to share/shwap/p2p/shrex/middleware.go index df0a690af7..c53a996eec 100644 --- a/share/p2p/middleware.go +++ b/share/shwap/p2p/shrex/middleware.go @@ -1,4 +1,4 @@ -package p2p +package shrex import ( "sync/atomic" diff --git a/share/p2p/params.go b/share/shwap/p2p/shrex/params.go similarity index 94% rename from share/p2p/params.go rename to share/shwap/p2p/shrex/params.go index 6636e38fc5..a5c0d5e3e2 100644 --- a/share/p2p/params.go +++ b/share/shwap/p2p/shrex/params.go @@ -1,4 +1,4 @@ -package p2p +package shrex import ( "fmt" @@ -7,6 +7,9 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" ) +// protocolString is the protocol string for the shrex protocol. +const ProtocolString = "/shrex/v0.1.0/" + // Parameters is the set of parameters that must be configured for the shrex/eds protocol. type Parameters struct { // ServerReadTimeout sets the timeout for reading messages from the stream. diff --git a/share/shwap/p2p/shrex/pb/shrex.pb.go b/share/shwap/p2p/shrex/pb/shrex.pb.go new file mode 100644 index 0000000000..88322b1985 --- /dev/null +++ b/share/shwap/p2p/shrex/pb/shrex.pb.go @@ -0,0 +1,336 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: share/shwap/p2p/shrex/pb/shrex.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Status int32 + +const ( + Status_INVALID Status = 0 + Status_OK Status = 1 + Status_NOT_FOUND Status = 2 + Status_INTERNAL Status = 3 +) + +var Status_name = map[int32]string{ + 0: "INVALID", + 1: "OK", + 2: "NOT_FOUND", + 3: "INTERNAL", +} + +var Status_value = map[string]int32{ + "INVALID": 0, + "OK": 1, + "NOT_FOUND": 2, + "INTERNAL": 3, +} + +func (x Status) String() string { + return proto.EnumName(Status_name, int32(x)) +} + +func (Status) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_68dfd78ade756110, []int{0} +} + +type Response struct { + Status Status `protobuf:"varint,1,opt,name=status,proto3,enum=Status" json:"status,omitempty"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { + return fileDescriptor_68dfd78ade756110, []int{0} +} +func (m *Response) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Response.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_Response.Merge(m, src) +} +func (m *Response) XXX_Size() int { + return m.Size() +} +func (m *Response) XXX_DiscardUnknown() { + xxx_messageInfo_Response.DiscardUnknown(m) +} + +var xxx_messageInfo_Response proto.InternalMessageInfo + +func (m *Response) GetStatus() Status { + if m != nil { + return m.Status + } + return Status_INVALID +} + +func init() { + proto.RegisterEnum("Status", Status_name, Status_value) + proto.RegisterType((*Response)(nil), "Response") +} + +func init() { + proto.RegisterFile("share/shwap/p2p/shrex/pb/shrex.proto", fileDescriptor_68dfd78ade756110) +} + +var fileDescriptor_68dfd78ade756110 = []byte{ + // 218 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x29, 0xce, 0x48, 0x2c, + 0x4a, 0xd5, 0x2f, 0xce, 0x28, 0x4f, 0x2c, 0xd0, 0x2f, 0x30, 0x2a, 0xd0, 0x2f, 0xce, 0x28, 0x4a, + 0xad, 0xd0, 0x2f, 0x48, 0x82, 0x30, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x95, 0xb4, 0xb9, 0x38, + 0x82, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0xe4, 0xb9, 0xd8, 0x8a, 0x4b, 0x12, 0x4b, + 0x4a, 0x8b, 0x25, 0x18, 0x15, 0x18, 0x35, 0xf8, 0x8c, 0xd8, 0xf5, 0x82, 0xc1, 0xdc, 0x20, 0xa8, + 0xb0, 0x96, 0x15, 0x17, 0x1b, 0x44, 0x44, 0x88, 0x9b, 0x8b, 0xdd, 0xd3, 0x2f, 0xcc, 0xd1, 0xc7, + 0xd3, 0x45, 0x80, 0x41, 0x88, 0x8d, 0x8b, 0xc9, 0xdf, 0x5b, 0x80, 0x51, 0x88, 0x97, 0x8b, 0xd3, + 0xcf, 0x3f, 0x24, 0xde, 0xcd, 0x3f, 0xd4, 0xcf, 0x45, 0x80, 0x49, 0x88, 0x87, 0x8b, 0xc3, 0xd3, + 0x2f, 0xc4, 0x35, 0xc8, 0xcf, 0xd1, 0x47, 0x80, 0xd9, 0x29, 0xfc, 0xc4, 0x23, 0x39, 0xc6, 0x0b, + 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, + 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x6c, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, + 0xf5, 0x93, 0x53, 0x73, 0x52, 0x8b, 0x4b, 0x32, 0x13, 0xf3, 0x8b, 0xd2, 0xe1, 0x6c, 0xdd, 0xbc, + 0xfc, 0x14, 0x90, 0x3f, 0xb0, 0xfb, 0x26, 0x89, 0x0d, 0xec, 0x11, 0x63, 0x40, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xd8, 0xb0, 0x4b, 0x58, 0xf0, 0x00, 0x00, 0x00, +} + +func (m *Response) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Response) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Response) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Status != 0 { + i = encodeVarintShrex(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintShrex(dAtA []byte, offset int, v uint64) int { + offset -= sovShrex(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Response) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Status != 0 { + n += 1 + sovShrex(uint64(m.Status)) + } + return n +} + +func sovShrex(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozShrex(x uint64) (n int) { + return sovShrex(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Response) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShrex + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShrex + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipShrex(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthShrex + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipShrex(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShrex + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShrex + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShrex + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthShrex + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupShrex + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthShrex + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthShrex = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowShrex = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupShrex = fmt.Errorf("proto: unexpected end of group") +) diff --git a/share/p2p/shrexeds/pb/extended_data_square.proto b/share/shwap/p2p/shrex/pb/shrex.proto similarity index 62% rename from share/p2p/shrexeds/pb/extended_data_square.proto rename to share/shwap/p2p/shrex/pb/shrex.proto index 63750962e9..7b3a57227d 100644 --- a/share/p2p/shrexeds/pb/extended_data_square.proto +++ b/share/shwap/p2p/shrex/pb/shrex.proto @@ -1,8 +1,6 @@ syntax = "proto3"; -message EDSRequest { - bytes hash = 1; // identifies the requested EDS. -} +option go_package = "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/pb"; enum Status { INVALID = 0; @@ -11,6 +9,6 @@ enum Status { INTERNAL = 3; // internal server error } -message EDSResponse { +message Response { Status status = 1; } diff --git a/share/p2p/peers/doc.go b/share/shwap/p2p/shrex/peers/doc.go similarity index 100% rename from share/p2p/peers/doc.go rename to share/shwap/p2p/shrex/peers/doc.go diff --git a/share/p2p/peers/manager.go b/share/shwap/p2p/shrex/peers/manager.go similarity index 99% rename from share/p2p/peers/manager.go rename to share/shwap/p2p/shrex/peers/manager.go index 0d2f6ac42b..857c5ee937 100644 --- a/share/p2p/peers/manager.go +++ b/share/shwap/p2p/shrex/peers/manager.go @@ -21,7 +21,7 @@ import ( "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) const ( diff --git a/share/p2p/peers/manager_test.go b/share/shwap/p2p/shrex/peers/manager_test.go similarity index 97% rename from share/p2p/peers/manager_test.go rename to share/shwap/p2p/shrex/peers/manager_test.go index 2a465dc59a..592f964e64 100644 --- a/share/p2p/peers/manager_test.go +++ b/share/shwap/p2p/shrex/peers/manager_test.go @@ -22,12 +22,12 @@ import ( "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/p2p/discovery" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/discovery" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) func TestManager(t *testing.T) { - t.Run("Validate pool by headerSub", func(t *testing.T) { + t.Run("Verify pool by headerSub", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) t.Cleanup(cancel) @@ -48,7 +48,7 @@ func TestManager(t *testing.T) { stopManager(t, manager) }) - t.Run("Validate pool by shrex.Getter", func(t *testing.T) { + t.Run("Verify pool by shrex.Getter", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) t.Cleanup(cancel) @@ -76,6 +76,7 @@ func TestManager(t *testing.T) { t.Cleanup(cancel) // create headerSub mock + h := testHeader() headerSub := newSubLock(h, nil) @@ -363,6 +364,7 @@ func TestIntegration(t *testing.T) { t.Run("get peer from discovery", func(t *testing.T) { fullNodesTag := "fullNodes" + version := "v1" nw, err := mocknet.FullMeshConnected(3) require.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) @@ -398,6 +400,7 @@ func TestIntegration(t *testing.T) { params, bnHost, routingdisc.NewRoutingDiscovery(bnRouter), + version, fullNodesTag, ) require.NoError(t, err) @@ -411,7 +414,7 @@ func TestIntegration(t *testing.T) { connGater, err := conngater.NewBasicConnectionGater(dssync.MutexWrap(datastore.NewMapDatastore())) require.NoError(t, err) fnPeerManager, err := NewManager( - DefaultParameters(), + *DefaultParameters(), nil, connGater, "test", @@ -434,6 +437,7 @@ func TestIntegration(t *testing.T) { params, fnHost, routingdisc.NewRoutingDiscovery(fnRouter), + version, fullNodesTag, discovery.WithOnPeersUpdate(fnPeerManager.UpdateNodePool), discovery.WithOnPeersUpdate(checkDiscoveredPeer), @@ -473,7 +477,7 @@ func testManager(ctx context.Context, headerSub libhead.Subscriber[*header.Exten return nil, err } manager, err := NewManager( - DefaultParameters(), + *DefaultParameters(), host, connGater, "test", diff --git a/share/p2p/peers/metrics.go b/share/shwap/p2p/shrex/peers/metrics.go similarity index 99% rename from share/p2p/peers/metrics.go rename to share/shwap/p2p/shrex/peers/metrics.go index b28b263127..d401d6a4fc 100644 --- a/share/p2p/peers/metrics.go +++ b/share/shwap/p2p/shrex/peers/metrics.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/metric" "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) const ( diff --git a/share/p2p/peers/options.go b/share/shwap/p2p/shrex/peers/options.go similarity index 95% rename from share/p2p/peers/options.go rename to share/shwap/p2p/shrex/peers/options.go index 2970dd2465..10957e7651 100644 --- a/share/p2p/peers/options.go +++ b/share/shwap/p2p/shrex/peers/options.go @@ -7,7 +7,7 @@ import ( libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/header" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" ) type Parameters struct { @@ -46,8 +46,8 @@ func (p *Parameters) Validate() error { } // DefaultParameters returns the default configuration values for the peer manager parameters -func DefaultParameters() Parameters { - return Parameters{ +func DefaultParameters() *Parameters { + return &Parameters{ // PoolValidationTimeout's default value is based on the default daser sampling timeout of 1 minute. // If a received datahash has not tried to be sampled within these two minutes, the pool will be // removed. diff --git a/share/p2p/peers/pool.go b/share/shwap/p2p/shrex/peers/pool.go similarity index 100% rename from share/p2p/peers/pool.go rename to share/shwap/p2p/shrex/peers/pool.go diff --git a/share/p2p/peers/pool_test.go b/share/shwap/p2p/shrex/peers/pool_test.go similarity index 100% rename from share/p2p/peers/pool_test.go rename to share/shwap/p2p/shrex/peers/pool_test.go diff --git a/share/p2p/peers/timedqueue.go b/share/shwap/p2p/shrex/peers/timedqueue.go similarity index 100% rename from share/p2p/peers/timedqueue.go rename to share/shwap/p2p/shrex/peers/timedqueue.go diff --git a/share/p2p/peers/timedqueue_test.go b/share/shwap/p2p/shrex/peers/timedqueue_test.go similarity index 100% rename from share/p2p/peers/timedqueue_test.go rename to share/shwap/p2p/shrex/peers/timedqueue_test.go diff --git a/share/p2p/recovery.go b/share/shwap/p2p/shrex/recovery.go similarity index 96% rename from share/p2p/recovery.go rename to share/shwap/p2p/shrex/recovery.go index b214969399..67bcb98d73 100644 --- a/share/p2p/recovery.go +++ b/share/shwap/p2p/shrex/recovery.go @@ -1,4 +1,4 @@ -package p2p +package shrex import ( "fmt" diff --git a/share/getters/shrex.go b/share/shwap/p2p/shrex/shrex_getter/shrex.go similarity index 69% rename from share/getters/shrex.go rename to share/shwap/p2p/shrex/shrex_getter/shrex.go index c59f5dbf6c..142b3f3ab2 100644 --- a/share/getters/shrex.go +++ b/share/shwap/p2p/shrex/shrex_getter/shrex.go @@ -1,4 +1,4 @@ -package getters +package shrex_getter //nolint:revive,stylecheck // underscore in pkg name will be fixed with shrex refactoring import ( "context" @@ -6,26 +6,34 @@ import ( "fmt" "time" + logging "github.com/ipfs/go-log/v2" libpeer "github.com/libp2p/go-libp2p/core/peer" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/libs/utils" - "github.com/celestiaorg/celestia-node/pruner" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/p2p" - "github.com/celestiaorg/celestia-node/share/p2p/peers" - "github.com/celestiaorg/celestia-node/share/p2p/shrexeds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexnd" + "github.com/celestiaorg/celestia-node/share/availability" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/peers" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexeds" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexnd" ) -var _ share.Getter = (*ShrexGetter)(nil) +var ( + tracer = otel.Tracer("shrex/getter") + meter = otel.Meter("shrex/getter") + log = logging.Logger("shrex/getter") +) + +var _ shwap.Getter = (*Getter)(nil) const ( // defaultMinRequestTimeout value is set according to observed time taken by healthy peer to @@ -34,8 +42,6 @@ const ( defaultMinAttemptsCount = 3 ) -var meter = otel.Meter("shrex/getter") - type metrics struct { edsAttempts metric.Int64Histogram ndAttempts metric.Int64Histogram @@ -61,7 +67,7 @@ func (m *metrics) recordNDAttempt(ctx context.Context, attemptCount int, success attribute.Bool("success", success))) } -func (sg *ShrexGetter) WithMetrics() error { +func (sg *Getter) WithMetrics() error { edsAttemptHistogram, err := meter.Int64Histogram( "getters_shrex_eds_attempts_per_request", metric.WithDescription("Number of attempts per shrex/eds request"), @@ -85,8 +91,8 @@ func (sg *ShrexGetter) WithMetrics() error { return nil } -// ShrexGetter is a share.Getter that uses the shrex/eds and shrex/nd protocol to retrieve shares. -type ShrexGetter struct { +// Getter is a share.Getter that uses the shrex/eds and shrex/nd protocol to retrieve shares. +type Getter struct { edsClient *shrexeds.Client ndClient *shrexnd.Client @@ -99,19 +105,19 @@ type ShrexGetter struct { // attempt multiple peers in scope of one request before context timeout is reached minAttemptsCount int - availabilityWindow pruner.AvailabilityWindow + availabilityWindow time.Duration metrics *metrics } -func NewShrexGetter( +func NewGetter( edsClient *shrexeds.Client, ndClient *shrexnd.Client, fullPeerManager *peers.Manager, archivalManager *peers.Manager, - availWindow pruner.AvailabilityWindow, -) *ShrexGetter { - s := &ShrexGetter{ + availWindow time.Duration, +) *Getter { + s := &Getter{ edsClient: edsClient, ndClient: ndClient, fullPeerManager: fullPeerManager, @@ -124,7 +130,7 @@ func NewShrexGetter( return s } -func (sg *ShrexGetter) Start(ctx context.Context) error { +func (sg *Getter) Start(ctx context.Context) error { err := sg.fullPeerManager.Start(ctx) if err != nil { return err @@ -132,7 +138,7 @@ func (sg *ShrexGetter) Start(ctx context.Context) error { return sg.archivalPeerManager.Start(ctx) } -func (sg *ShrexGetter) Stop(ctx context.Context) error { +func (sg *Getter) Stop(ctx context.Context) error { err := sg.fullPeerManager.Stop(ctx) if err != nil { return err @@ -140,11 +146,15 @@ func (sg *ShrexGetter) Stop(ctx context.Context) error { return sg.archivalPeerManager.Stop(ctx) } -func (sg *ShrexGetter) GetShare(context.Context, *header.ExtendedHeader, int, int) (share.Share, error) { - return nil, fmt.Errorf("getter/shrex: GetShare %w", errOperationNotSupported) +func (sg *Getter) GetSamples(context.Context, *header.ExtendedHeader, []shwap.SampleCoords) ([]shwap.Sample, error) { + return nil, fmt.Errorf("getter/shrex: GetShare %w", shwap.ErrOperationNotSupported) +} + +func (sg *Getter) GetRow(_ context.Context, _ *header.ExtendedHeader, _ int) (shwap.Row, error) { + return shwap.Row{}, fmt.Errorf("getter/shrex: GetRow %w", shwap.ErrOperationNotSupported) } -func (sg *ShrexGetter) GetEDS(ctx context.Context, header *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { +func (sg *Getter) GetEDS(ctx context.Context, header *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { var err error ctx, span := tracer.Start(ctx, "shrex/get-eds") defer func() { @@ -152,8 +162,8 @@ func (sg *ShrexGetter) GetEDS(ctx context.Context, header *header.ExtendedHeader }() // short circuit if the data root is empty - if header.DAH.Equals(share.EmptyRoot()) { - return share.EmptyExtendedDataSquare(), nil + if header.DAH.Equals(share.EmptyEDSRoots()) { + return share.EmptyEDS(), nil } var attempt int @@ -176,8 +186,8 @@ func (sg *ShrexGetter) GetEDS(ctx context.Context, header *header.ExtendedHeader } reqStart := time.Now() - reqCtx, cancel := ctxWithSplitTimeout(ctx, sg.minAttemptsCount-attempt+1, sg.minRequestTimeout) - eds, getErr := sg.edsClient.RequestEDS(reqCtx, header.DAH.Hash(), peer) + reqCtx, cancel := utils.CtxWithSplitTimeout(ctx, sg.minAttemptsCount-attempt+1, sg.minRequestTimeout) + eds, getErr := sg.edsClient.RequestEDS(reqCtx, header.DAH, header.Height(), peer) cancel() switch { case getErr == nil: @@ -187,16 +197,16 @@ func (sg *ShrexGetter) GetEDS(ctx context.Context, header *header.ExtendedHeader case errors.Is(getErr, context.DeadlineExceeded), errors.Is(getErr, context.Canceled): setStatus(peers.ResultCooldownPeer) - case errors.Is(getErr, p2p.ErrNotFound): - getErr = share.ErrNotFound + case errors.Is(getErr, shrex.ErrNotFound): + getErr = shwap.ErrNotFound setStatus(peers.ResultCooldownPeer) - case errors.Is(getErr, p2p.ErrInvalidResponse): + case errors.Is(getErr, shrex.ErrInvalidResponse): setStatus(peers.ResultBlacklistPeer) default: setStatus(peers.ResultCooldownPeer) } - if !ErrorContains(err, getErr) { + if !shrex.ErrorContains(err, getErr) { err = errors.Join(err, getErr) } log.Debugw("eds: request failed", @@ -208,11 +218,11 @@ func (sg *ShrexGetter) GetEDS(ctx context.Context, header *header.ExtendedHeader } } -func (sg *ShrexGetter) GetSharesByNamespace( +func (sg *Getter) GetNamespaceData( ctx context.Context, header *header.ExtendedHeader, - namespace share.Namespace, -) (share.NamespacedShares, error) { + namespace libshare.Namespace, +) (shwap.NamespaceData, error) { if err := namespace.ValidateForData(); err != nil { return nil, err } @@ -229,9 +239,12 @@ func (sg *ShrexGetter) GetSharesByNamespace( // verify that the namespace could exist inside the roots before starting network requests dah := header.DAH - roots := ipld.FilterRootByNamespace(dah, namespace) - if len(roots) == 0 { - return []share.NamespacedRow{}, nil + rowIdxs, err := share.RowsWithNamespace(dah, namespace) + if err != nil { + return nil, err + } + if len(rowIdxs) == 0 { + return shwap.NamespaceData{}, nil } for { @@ -254,8 +267,8 @@ func (sg *ShrexGetter) GetSharesByNamespace( } reqStart := time.Now() - reqCtx, cancel := ctxWithSplitTimeout(ctx, sg.minAttemptsCount-attempt+1, sg.minRequestTimeout) - nd, getErr := sg.ndClient.RequestND(reqCtx, dah, namespace, peer) + reqCtx, cancel := utils.CtxWithSplitTimeout(ctx, sg.minAttemptsCount-attempt+1, sg.minRequestTimeout) + nd, getErr := sg.ndClient.RequestND(reqCtx, header.Height(), namespace, peer) cancel() switch { case getErr == nil: @@ -271,16 +284,16 @@ func (sg *ShrexGetter) GetSharesByNamespace( case errors.Is(getErr, context.DeadlineExceeded), errors.Is(getErr, context.Canceled): setStatus(peers.ResultCooldownPeer) - case errors.Is(getErr, p2p.ErrNotFound): - getErr = share.ErrNotFound + case errors.Is(getErr, shrex.ErrNotFound): + getErr = shwap.ErrNotFound setStatus(peers.ResultCooldownPeer) - case errors.Is(getErr, p2p.ErrInvalidResponse): + case errors.Is(getErr, shrex.ErrInvalidResponse): setStatus(peers.ResultBlacklistPeer) default: setStatus(peers.ResultCooldownPeer) } - if !ErrorContains(err, getErr) { + if !shrex.ErrorContains(err, getErr) { err = errors.Join(err, getErr) } log.Debugw("nd: request failed", @@ -293,11 +306,11 @@ func (sg *ShrexGetter) GetSharesByNamespace( } } -func (sg *ShrexGetter) getPeer( +func (sg *Getter) getPeer( ctx context.Context, header *header.ExtendedHeader, ) (libpeer.ID, peers.DoneFunc, error) { - if !pruner.IsWithinAvailabilityWindow(header.Time(), sg.availabilityWindow) { + if !availability.IsWithinWindow(header.Time(), sg.availabilityWindow) { p, df, err := sg.archivalPeerManager.Peer(ctx, header.DAH.Hash(), header.Height()) return p, df, err } diff --git a/share/getters/shrex_test.go b/share/shwap/p2p/shrex/shrex_getter/shrex_test.go similarity index 51% rename from share/getters/shrex_test.go rename to share/shwap/p2p/shrex/shrex_getter/shrex_test.go index a474e1e618..25298a940c 100644 --- a/share/getters/shrex_test.go +++ b/share/shwap/p2p/shrex/shrex_getter/shrex_test.go @@ -1,9 +1,8 @@ -package getters +package shrex_getter //nolint:stylecheck // underscore in pkg name will be fixed with shrex refactoring import ( "context" - "encoding/binary" - "errors" + "sync/atomic" "testing" "time" @@ -16,22 +15,21 @@ import ( "github.com/stretchr/testify/require" libhead "github.com/celestiaorg/go-header" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/nmt" "github.com/celestiaorg/rsmt2d" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/header/headertest" - "github.com/celestiaorg/celestia-node/pruner/full" - "github.com/celestiaorg/celestia-node/pruner/light" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/availability" "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/ipld" - "github.com/celestiaorg/celestia-node/share/p2p/peers" - "github.com/celestiaorg/celestia-node/share/p2p/shrexeds" - "github.com/celestiaorg/celestia-node/share/p2p/shrexnd" - "github.com/celestiaorg/celestia-node/share/p2p/shrexsub" - "github.com/celestiaorg/celestia-node/share/sharetest" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/peers" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexeds" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexnd" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub" + "github.com/celestiaorg/celestia-node/store" ) func TestShrexGetter(t *testing.T) { @@ -46,8 +44,6 @@ func TestShrexGetter(t *testing.T) { // launch eds store and put test data into it edsStore, err := newStore(t) require.NoError(t, err) - err = edsStore.Start(ctx) - require.NoError(t, err) ndClient, _ := newNDClientServer(ctx, t, edsStore, srvHost, clHost) edsClient, _ := newEDSClientServer(ctx, t, edsStore, srvHost, clHost) @@ -60,26 +56,34 @@ func TestShrexGetter(t *testing.T) { archivalPeerManager, err := testManager(ctx, clHost, sub) require.NoError(t, err) - getter := NewShrexGetter(edsClient, ndClient, fullPeerManager, archivalPeerManager, light.Window) + getter := NewGetter(edsClient, ndClient, fullPeerManager, archivalPeerManager, availability.RequestWindow) require.NoError(t, getter.Start(ctx)) + height := atomic.Uint64{} + height.Add(1) + t.Run("ND_Available, total data size > 1mb", func(t *testing.T) { ctx, cancel := context.WithTimeout(ctx, time.Second*10) t.Cleanup(cancel) // generate test data - namespace := sharetest.RandV0Namespace() - randEDS, dah := edstest.RandEDSWithNamespace(t, namespace, 64) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - require.NoError(t, edsStore.Put(ctx, dah.Hash(), randEDS)) + size := 64 + namespace := libshare.RandomNamespace() + height := height.Add(1) + randEDS, roots := edstest.RandEDSWithNamespace(t, namespace, size*size, size) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + eh.RawHeader.Height = int64(height) + + err = edsStore.PutODSQ4(ctx, roots, height, randEDS) + require.NoError(t, err) fullPeerManager.Validate(ctx, srvHost.ID(), shrexsub.Notification{ - DataHash: dah.Hash(), - Height: 1, + DataHash: roots.Hash(), + Height: height, }) - got, err := getter.GetSharesByNamespace(ctx, eh, namespace) + got, err := getter.GetNamespaceData(ctx, eh, namespace) require.NoError(t, err) - require.NoError(t, got.Verify(dah, namespace)) + require.NoError(t, got.Verify(roots, namespace)) }) t.Run("ND_err_not_found", func(t *testing.T) { @@ -87,15 +91,18 @@ func TestShrexGetter(t *testing.T) { t.Cleanup(cancel) // generate test data - _, dah, namespace := generateTestEDS(t) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) + height := height.Add(1) + _, roots, namespace := generateTestEDS(t) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + eh.RawHeader.Height = int64(height) + fullPeerManager.Validate(ctx, srvHost.ID(), shrexsub.Notification{ - DataHash: dah.Hash(), - Height: 1, + DataHash: roots.Hash(), + Height: height, }) - _, err := getter.GetSharesByNamespace(ctx, eh, namespace) - require.ErrorIs(t, err, share.ErrNotFound) + _, err := getter.GetNamespaceData(ctx, eh, namespace) + require.ErrorIs(t, err, shwap.ErrNotFound) }) t.Run("ND_namespace_not_included", func(t *testing.T) { @@ -103,37 +110,45 @@ func TestShrexGetter(t *testing.T) { t.Cleanup(cancel) // generate test data - eds, dah, maxNamespace := generateTestEDS(t) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - require.NoError(t, edsStore.Put(ctx, dah.Hash(), eds)) + height := height.Add(1) + eds, roots, maxNamespace := generateTestEDS(t) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + eh.RawHeader.Height = int64(height) + + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) fullPeerManager.Validate(ctx, srvHost.ID(), shrexsub.Notification{ - DataHash: dah.Hash(), - Height: 1, + DataHash: roots.Hash(), + Height: height, }) // namespace inside root range - nID, err := addToNamespace(maxNamespace, -1) + nID, err := maxNamespace.AddInt(-1) require.NoError(t, err) // check for namespace to be between max and min namespace in root - require.Len(t, ipld.FilterRootByNamespace(dah, nID), 1) + rows, err := share.RowsWithNamespace(roots, nID) + require.NoError(t, err) + require.Len(t, rows, 1) - emptyShares, err := getter.GetSharesByNamespace(ctx, eh, nID) + emptyShares, err := getter.GetNamespaceData(ctx, eh, nID) require.NoError(t, err) // no shares should be returned require.Nil(t, emptyShares.Flatten()) - require.Nil(t, emptyShares.Verify(dah, nID)) + require.Nil(t, emptyShares.Verify(roots, nID)) // namespace outside root range - nID, err = addToNamespace(maxNamespace, 1) + nID, err = maxNamespace.AddInt(1) require.NoError(t, err) // check for namespace to be not in root - require.Len(t, ipld.FilterRootByNamespace(dah, nID), 0) + rows, err = share.RowsWithNamespace(roots, nID) + require.NoError(t, err) + require.Len(t, rows, 0) - emptyShares, err = getter.GetSharesByNamespace(ctx, eh, nID) + emptyShares, err = getter.GetNamespaceData(ctx, eh, nID) require.NoError(t, err) // no shares should be returned require.Nil(t, emptyShares.Flatten()) - require.Nil(t, emptyShares.Verify(dah, nID)) + require.Nil(t, emptyShares.Verify(roots, nID)) }) t.Run("ND_namespace_not_in_dah", func(t *testing.T) { @@ -141,24 +156,30 @@ func TestShrexGetter(t *testing.T) { t.Cleanup(cancel) // generate test data - eds, dah, maxNamespace := generateTestEDS(t) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - require.NoError(t, edsStore.Put(ctx, dah.Hash(), eds)) + eds, roots, maxNamespace := generateTestEDS(t) + height := height.Add(1) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + eh.RawHeader.Height = int64(height) + + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) fullPeerManager.Validate(ctx, srvHost.ID(), shrexsub.Notification{ - DataHash: dah.Hash(), - Height: 1, + DataHash: roots.Hash(), + Height: height, }) - namespace, err := addToNamespace(maxNamespace, 1) + namespace, err := maxNamespace.AddInt(1) require.NoError(t, err) // check for namespace to be not in root - require.Len(t, ipld.FilterRootByNamespace(dah, namespace), 0) + rows, err := share.RowsWithNamespace(roots, namespace) + require.NoError(t, err) + require.Len(t, rows, 0) - emptyShares, err := getter.GetSharesByNamespace(ctx, eh, namespace) + emptyShares, err := getter.GetNamespaceData(ctx, eh, namespace) require.NoError(t, err) // no shares should be returned require.Empty(t, emptyShares.Flatten()) - require.Nil(t, emptyShares.Verify(dah, namespace)) + require.Nil(t, emptyShares.Verify(roots, namespace)) }) t.Run("EDS_Available", func(t *testing.T) { @@ -166,12 +187,16 @@ func TestShrexGetter(t *testing.T) { t.Cleanup(cancel) // generate test data - randEDS, dah, _ := generateTestEDS(t) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) - require.NoError(t, edsStore.Put(ctx, dah.Hash(), randEDS)) + randEDS, roots, _ := generateTestEDS(t) + height := height.Add(1) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + eh.RawHeader.Height = int64(height) + + err = edsStore.PutODSQ4(ctx, roots, height, randEDS) + require.NoError(t, err) fullPeerManager.Validate(ctx, srvHost.ID(), shrexsub.Notification{ - DataHash: dah.Hash(), - Height: 1, + DataHash: roots.Hash(), + Height: height, }) got, err := getter.GetEDS(ctx, eh) @@ -183,11 +208,14 @@ func TestShrexGetter(t *testing.T) { ctx, cancel := context.WithTimeout(ctx, time.Second) // generate test data - _, dah, _ := generateTestEDS(t) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) + _, roots, _ := generateTestEDS(t) + height := height.Add(1) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + eh.RawHeader.Height = int64(height) + fullPeerManager.Validate(ctx, srvHost.ID(), shrexsub.Notification{ - DataHash: dah.Hash(), - Height: 1, + DataHash: roots.Hash(), + Height: height, }) cancel() @@ -200,15 +228,18 @@ func TestShrexGetter(t *testing.T) { t.Cleanup(cancel) // generate test data - _, dah, _ := generateTestEDS(t) - eh := headertest.RandExtendedHeaderWithRoot(t, dah) + _, roots, _ := generateTestEDS(t) + height := height.Add(1) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + eh.RawHeader.Height = int64(height) + fullPeerManager.Validate(ctx, srvHost.ID(), shrexsub.Notification{ - DataHash: dah.Hash(), - Height: 1, + DataHash: roots.Hash(), + Height: height, }) _, err := getter.GetEDS(ctx, eh) - require.ErrorIs(t, err, share.ErrNotFound) + require.ErrorIs(t, err, shwap.ErrNotFound) }) // tests getPeer's ability to route requests based on whether @@ -225,10 +256,12 @@ func TestShrexGetter(t *testing.T) { getter.archivalPeerManager.UpdateNodePool(archivalPeer.ID(), true) getter.fullPeerManager.UpdateNodePool(fullPeer.ID(), true) + height := height.Add(1) eh := headertest.RandExtendedHeader(t) + eh.RawHeader.Height = int64(height) // historical data expects an archival peer - eh.RawHeader.Time = time.Now().Add(-(time.Duration(full.Window) + time.Second)) + eh.RawHeader.Time = time.Now().Add(-(availability.StorageWindow + time.Second)) id, _, err := getter.getPeer(ctx, eh) require.NoError(t, err) assert.Equal(t, archivalPeer.ID(), id) @@ -241,19 +274,20 @@ func TestShrexGetter(t *testing.T) { }) } -func newStore(t *testing.T) (*eds.Store, error) { +func newStore(t *testing.T) (*store.Store, error) { t.Helper() - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - return eds.NewStore(eds.DefaultParameters(), t.TempDir(), ds) + return store.NewStore(store.DefaultParameters(), t.TempDir()) } -func generateTestEDS(t *testing.T) (*rsmt2d.ExtendedDataSquare, *share.Root, share.Namespace) { +func generateTestEDS(t *testing.T) (*rsmt2d.ExtendedDataSquare, *share.AxisRoots, libshare.Namespace) { eds := edstest.RandEDS(t, 4) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + max := nmt.MaxNamespace(roots.RowRoots[(len(roots.RowRoots))/2-1], libshare.NamespaceSize) + ns, err := libshare.NewNamespaceFromBytes(max) require.NoError(t, err) - max := nmt.MaxNamespace(dah.RowRoots[(len(dah.RowRoots))/2-1], share.NamespaceSize) - return eds, dah, max + return eds, roots, ns } func testManager( @@ -269,7 +303,7 @@ func testManager( return nil, err } manager, err := peers.NewManager( - peers.DefaultParameters(), + *peers.DefaultParameters(), host, connGater, "test", @@ -279,7 +313,7 @@ func testManager( } func newNDClientServer( - ctx context.Context, t *testing.T, edsStore *eds.Store, srvHost, clHost host.Host, + ctx context.Context, t *testing.T, edsStore *store.Store, srvHost, clHost host.Host, ) (*shrexnd.Client, *shrexnd.Server) { params := shrexnd.DefaultParameters() @@ -299,7 +333,7 @@ func newNDClientServer( } func newEDSClientServer( - ctx context.Context, t *testing.T, edsStore *eds.Store, srvHost, clHost host.Host, + ctx context.Context, t *testing.T, edsStore *store.Store, srvHost, clHost host.Host, ) (*shrexeds.Client, *shrexeds.Server) { params := shrexeds.DefaultParameters() @@ -317,103 +351,3 @@ func newEDSClientServer( require.NoError(t, err) return client, server } - -// addToNamespace adds arbitrary int value to namespace, treating namespace as big-endian -// implementation of int -func addToNamespace(namespace share.Namespace, val int) (share.Namespace, error) { - if val == 0 { - return namespace, nil - } - // Convert the input integer to a byte slice and add it to result slice - result := make([]byte, len(namespace)) - if val > 0 { - binary.BigEndian.PutUint64(result[len(namespace)-8:], uint64(val)) - } else { - binary.BigEndian.PutUint64(result[len(namespace)-8:], uint64(-val)) - } - - // Perform addition byte by byte - var carry int - for i := len(namespace) - 1; i >= 0; i-- { - var sum int - if val > 0 { - sum = int(namespace[i]) + int(result[i]) + carry - } else { - sum = int(namespace[i]) - int(result[i]) + carry - } - - switch { - case sum > 255: - carry = 1 - sum -= 256 - case sum < 0: - carry = -1 - sum += 256 - default: - carry = 0 - } - - result[i] = uint8(sum) - } - - // Handle any remaining carry - if carry != 0 { - return nil, errors.New("namespace overflow") - } - - return result, nil -} - -func TestAddToNamespace(t *testing.T) { - testCases := []struct { - name string - value int - input share.Namespace - expected share.Namespace - expectedError error - }{ - { - name: "Positive value addition", - value: 42, - input: share.Namespace{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, - expected: share.Namespace{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2b}, - expectedError: nil, - }, - { - name: "Negative value addition", - value: -42, - input: share.Namespace{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, - expected: share.Namespace{0x1, 0x1, 0x1, 0x1, 0x1, 0x01, 0x1, 0x1, 0x1, 0x0, 0xd7}, - expectedError: nil, - }, - { - name: "Overflow error", - value: 1, - input: share.Namespace{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - expected: nil, - expectedError: errors.New("namespace overflow"), - }, - { - name: "Overflow error negative", - value: -1, - input: share.Namespace{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - expected: nil, - expectedError: errors.New("namespace overflow"), - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - result, err := addToNamespace(tc.input, tc.value) - if tc.expectedError == nil { - require.NoError(t, err) - require.Equal(t, tc.expected, result) - return - } - require.Error(t, err) - if err.Error() != tc.expectedError.Error() { - t.Errorf("Unexpected error message. Expected: %v, Got: %v", tc.expectedError, err) - } - }) - } -} diff --git a/share/p2p/shrexeds/client.go b/share/shwap/p2p/shrex/shrexeds/client.go similarity index 65% rename from share/p2p/shrexeds/client.go rename to share/shwap/p2p/shrex/shrexeds/client.go index d56e0e20f5..5239c407f9 100644 --- a/share/p2p/shrexeds/client.go +++ b/share/shwap/p2p/shrex/shrexeds/client.go @@ -16,10 +16,12 @@ import ( "github.com/celestiaorg/go-libp2p-messenger/serde" "github.com/celestiaorg/rsmt2d" + "github.com/celestiaorg/celestia-node/libs/utils" "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/share/eds" - "github.com/celestiaorg/celestia-node/share/p2p" - pb "github.com/celestiaorg/celestia-node/share/p2p/shrexeds/pb" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" + shrexpb "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/pb" ) // Client is responsible for requesting EDSs for blocksync over the ShrEx/EDS protocol. @@ -28,7 +30,7 @@ type Client struct { protocolID protocol.ID host host.Host - metrics *p2p.Metrics + metrics *shrex.Metrics } // NewClient creates a new ShrEx/EDS client. @@ -40,23 +42,27 @@ func NewClient(params *Parameters, host host.Host) (*Client, error) { return &Client{ params: params, host: host, - protocolID: p2p.ProtocolID(params.NetworkID(), protocolString), + protocolID: shrex.ProtocolID(params.NetworkID(), protocolString), }, nil } // RequestEDS requests the ODS from the given peers and returns the EDS upon success. func (c *Client) RequestEDS( ctx context.Context, - dataHash share.DataHash, + root *share.AxisRoots, + height uint64, peer peer.ID, ) (*rsmt2d.ExtendedDataSquare, error) { - eds, err := c.doRequest(ctx, dataHash, peer) + eds, err := c.doRequest(ctx, root, height, peer) if err == nil { return eds, nil } - log.Debugw("client: eds request to peer failed", "peer", peer.String(), "hash", dataHash.String(), "error", err) + log.Debugw("client: eds request to peer failed", + "height", height, + "peer", peer.String(), + "error", err) if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { - c.metrics.ObserveRequests(ctx, 1, p2p.StatusTimeout) + c.metrics.ObserveRequests(ctx, 1, shrex.StatusTimeout) return nil, err } // some net.Errors also mean the context deadline was exceeded, but yamux/mocknet do not @@ -64,14 +70,14 @@ func (c *Client) RequestEDS( var ne net.Error if errors.As(err, &ne) && ne.Timeout() { if deadline, _ := ctx.Deadline(); deadline.Before(time.Now()) { - c.metrics.ObserveRequests(ctx, 1, p2p.StatusTimeout) + c.metrics.ObserveRequests(ctx, 1, shrex.StatusTimeout) return nil, context.DeadlineExceeded } } - if !errors.Is(err, p2p.ErrNotFound) { + if !errors.Is(err, shrex.ErrNotFound) { log.Warnw("client: eds request to peer failed", "peer", peer.String(), - "hash", dataHash.String(), + "height", height, "err", err) } @@ -80,35 +86,39 @@ func (c *Client) RequestEDS( func (c *Client) doRequest( ctx context.Context, - dataHash share.DataHash, + root *share.AxisRoots, + height uint64, to peer.ID, ) (*rsmt2d.ExtendedDataSquare, error) { streamOpenCtx, cancel := context.WithTimeout(ctx, c.params.ServerReadTimeout) defer cancel() stream, err := c.host.NewStream(streamOpenCtx, to, c.protocolID) if err != nil { - return nil, fmt.Errorf("failed to open stream: %w", err) + return nil, fmt.Errorf("open stream: %w", err) } - defer stream.Close() + defer utils.CloseAndLog(log, "client", stream) c.setStreamDeadlines(ctx, stream) - - req := &pb.EDSRequest{Hash: dataHash} - // request ODS - log.Debugw("client: requesting ods", "hash", dataHash.String(), "peer", to.String()) - _, err = serde.Write(stream, req) + log.Debugw("client: requesting ods", + "height", height, + "peer", to.String()) + id, err := shwap.NewEdsID(height) + if err != nil { + return nil, fmt.Errorf("create request: %w", err) + } + _, err = id.WriteTo(stream) if err != nil { - stream.Reset() //nolint:errcheck - return nil, fmt.Errorf("failed to write request to stream: %w", err) + return nil, fmt.Errorf("write request to stream: %w", err) } + err = stream.CloseWrite() if err != nil { - log.Debugw("client: error closing write", "err", err) + log.Warnw("client: error closing write", "err", err) } // read and parse status from peer - resp := new(pb.EDSResponse) + resp := new(shrexpb.Response) err = stream.SetReadDeadline(time.Now().Add(c.params.ServerReadTimeout)) if err != nil { log.Debugw("client: failed to set read deadline for reading status", "err", err) @@ -117,35 +127,33 @@ func (c *Client) doRequest( if err != nil { // server closes the stream here if we are rate limited if errors.Is(err, io.EOF) { - c.metrics.ObserveRequests(ctx, 1, p2p.StatusRateLimited) - return nil, p2p.ErrNotFound + c.metrics.ObserveRequests(ctx, 1, shrex.StatusRateLimited) + return nil, shrex.ErrNotFound } - stream.Reset() //nolint:errcheck - return nil, fmt.Errorf("failed to read status from stream: %w", err) + return nil, fmt.Errorf("read status from stream: %w", err) } - switch resp.Status { - case pb.Status_OK: + case shrexpb.Status_OK: // reset stream deadlines to original values, since read deadline was changed during status read c.setStreamDeadlines(ctx, stream) // use header and ODS bytes to construct EDS and verify it against dataHash - eds, err := eds.ReadEDS(ctx, stream, dataHash) + eds, err := eds.ReadAccessor(ctx, stream, root) if err != nil { - return nil, fmt.Errorf("failed to read eds from ods bytes: %w", err) + return nil, fmt.Errorf("read eds from stream: %w", err) } - c.metrics.ObserveRequests(ctx, 1, p2p.StatusSuccess) - return eds, nil - case pb.Status_NOT_FOUND: - c.metrics.ObserveRequests(ctx, 1, p2p.StatusNotFound) - return nil, p2p.ErrNotFound - case pb.Status_INVALID: + c.metrics.ObserveRequests(ctx, 1, shrex.StatusSuccess) + return eds.ExtendedDataSquare, nil + case shrexpb.Status_NOT_FOUND: + c.metrics.ObserveRequests(ctx, 1, shrex.StatusNotFound) + return nil, shrex.ErrNotFound + case shrexpb.Status_INVALID: log.Debug("client: invalid request") fallthrough - case pb.Status_INTERNAL: + case shrexpb.Status_INTERNAL: fallthrough default: - c.metrics.ObserveRequests(ctx, 1, p2p.StatusInternalErr) - return nil, p2p.ErrInvalidResponse + c.metrics.ObserveRequests(ctx, 1, shrex.StatusInternalErr) + return nil, shrex.ErrInvalidResponse } } diff --git a/share/p2p/shrexeds/doc.go b/share/shwap/p2p/shrex/shrexeds/doc.go similarity index 100% rename from share/p2p/shrexeds/doc.go rename to share/shwap/p2p/shrex/shrexeds/doc.go diff --git a/share/p2p/shrexeds/exchange_test.go b/share/shwap/p2p/shrex/shrexeds/exchange_test.go similarity index 65% rename from share/p2p/shrexeds/exchange_test.go rename to share/shwap/p2p/shrex/shrexeds/exchange_test.go index 9155be6dec..89e1dafa1c 100644 --- a/share/p2p/shrexeds/exchange_test.go +++ b/share/shwap/p2p/shrex/shrexeds/exchange_test.go @@ -6,8 +6,6 @@ import ( "testing" "time" - "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" libhost "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" @@ -15,9 +13,10 @@ import ( "github.com/stretchr/testify/require" "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/p2p" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" + "github.com/celestiaorg/celestia-node/store" ) func TestExchange_RequestEDS(t *testing.T) { @@ -25,21 +24,19 @@ func TestExchange_RequestEDS(t *testing.T) { t.Cleanup(cancel) store, client, server := makeExchange(t) - err := store.Start(ctx) - require.NoError(t, err) - - err = server.Start(ctx) + err := server.Start(ctx) require.NoError(t, err) // Testcase: EDS is immediately available t.Run("EDS_Available", func(t *testing.T) { eds := edstest.RandEDS(t, 4) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - err = store.Put(ctx, dah.Hash(), eds) + height := uint64(1) + err = store.PutODSQ4(ctx, roots, height, eds) require.NoError(t, err) - requestedEDS, err := client.RequestEDS(ctx, dah.Hash(), server.host.ID()) + requestedEDS, err := client.RequestEDS(ctx, roots, height, server.host.ID()) assert.NoError(t, err) assert.Equal(t, eds.Flattened(), requestedEDS.Flattened()) }) @@ -47,19 +44,20 @@ func TestExchange_RequestEDS(t *testing.T) { // Testcase: EDS is unavailable initially, but is found after multiple requests t.Run("EDS_AvailableAfterDelay", func(t *testing.T) { eds := edstest.RandEDS(t, 4) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) + height := uint64(666) lock := make(chan struct{}) go func() { <-lock - err = store.Put(ctx, dah.Hash(), eds) + err := store.PutODSQ4(ctx, roots, height, eds) require.NoError(t, err) lock <- struct{}{} }() - requestedEDS, err := client.RequestEDS(ctx, dah.Hash(), server.host.ID()) - assert.ErrorIs(t, err, p2p.ErrNotFound) + requestedEDS, err := client.RequestEDS(ctx, roots, height, server.host.ID()) + assert.ErrorIs(t, err, shrex.ErrNotFound) assert.Nil(t, requestedEDS) // unlock write @@ -67,16 +65,17 @@ func TestExchange_RequestEDS(t *testing.T) { // wait for write to finish <-lock - requestedEDS, err = client.RequestEDS(ctx, dah.Hash(), server.host.ID()) + requestedEDS, err = client.RequestEDS(ctx, roots, height, server.host.ID()) assert.NoError(t, err) assert.Equal(t, eds.Flattened(), requestedEDS.Flattened()) }) // Testcase: Invalid request excludes peer from round-robin, stopping request t.Run("EDS_InvalidRequest", func(t *testing.T) { - dataHash := []byte("invalid") - requestedEDS, err := client.RequestEDS(ctx, dataHash, server.host.ID()) - assert.ErrorContains(t, err, "stream reset") + emptyRoot := share.EmptyEDSRoots() + height := uint64(0) + requestedEDS, err := client.RequestEDS(ctx, emptyRoot, height, server.host.ID()) + assert.ErrorIs(t, err, shwap.ErrInvalidID) assert.Nil(t, requestedEDS) }) @@ -84,17 +83,17 @@ func TestExchange_RequestEDS(t *testing.T) { timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) t.Cleanup(cancel) eds := edstest.RandEDS(t, 4) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - _, err = client.RequestEDS(timeoutCtx, dah.Hash(), server.host.ID()) - require.ErrorIs(t, err, p2p.ErrNotFound) + height := uint64(668) + _, err = client.RequestEDS(timeoutCtx, roots, height, server.host.ID()) + require.ErrorIs(t, err, shrex.ErrNotFound) }) // Testcase: Concurrency limit reached t.Run("EDS_concurrency_limit", func(t *testing.T) { - store, client, server := makeExchange(t) + _, client, server := makeExchange(t) - require.NoError(t, store.Start(ctx)) require.NoError(t, server.Start(ctx)) ctx, cancel := context.WithTimeout(ctx, time.Second) @@ -115,34 +114,25 @@ func TestExchange_RequestEDS(t *testing.T) { t.Fatal("timeout") } } - middleware := p2p.NewMiddleware(rateLimit) + middleware := shrex.NewMiddleware(rateLimit) server.host.SetStreamHandler(server.protocolID, middleware.RateLimitHandler(mockHandler)) // take server concurrency slots with blocked requests + emptyRoot := share.EmptyEDSRoots() for i := 0; i < rateLimit; i++ { go func(i int) { - client.RequestEDS(ctx, nil, server.host.ID()) //nolint:errcheck + client.RequestEDS(ctx, emptyRoot, 1, server.host.ID()) //nolint:errcheck }(i) } // wait until all server slots are taken wg.Wait() - _, err = client.RequestEDS(ctx, nil, server.host.ID()) - require.ErrorIs(t, err, p2p.ErrNotFound) + _, err = client.RequestEDS(ctx, emptyRoot, 1, server.host.ID()) + require.ErrorIs(t, err, shrex.ErrNotFound) }) } -func newStore(t *testing.T) *eds.Store { - t.Helper() - - storeCfg := eds.DefaultParameters() - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - store, err := eds.NewStore(storeCfg, t.TempDir(), ds) - require.NoError(t, err) - return store -} - func createMocknet(t *testing.T, amount int) []libhost.Host { t.Helper() @@ -152,9 +142,10 @@ func createMocknet(t *testing.T, amount int) []libhost.Host { return net.Hosts() } -func makeExchange(t *testing.T) (*eds.Store, *Client, *Server) { +func makeExchange(t *testing.T) (*store.Store, *Client, *Server) { t.Helper() - store := newStore(t) + store, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) hosts := createMocknet(t, 2) client, err := NewClient(DefaultParameters(), hosts[0]) diff --git a/share/p2p/shrexeds/params.go b/share/shwap/p2p/shrex/shrexeds/params.go similarity index 73% rename from share/p2p/shrexeds/params.go rename to share/shwap/p2p/shrex/shrexeds/params.go index 795cb313ed..c5d76fecb2 100644 --- a/share/p2p/shrexeds/params.go +++ b/share/shwap/p2p/shrex/shrexeds/params.go @@ -5,16 +5,17 @@ import ( logging "github.com/ipfs/go-log/v2" - "github.com/celestiaorg/celestia-node/share/p2p" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" ) -const protocolString = "/shrex/eds/v0.0.1" +const protocolString = shrex.ProtocolString + shwap.EDSName var log = logging.Logger("shrex/eds") // Parameters is the set of parameters that must be configured for the shrex/eds protocol. type Parameters struct { - *p2p.Parameters + *shrex.Parameters // BufferSize defines the size of the buffer used for writing an ODS over the stream. BufferSize uint64 @@ -22,7 +23,7 @@ type Parameters struct { func DefaultParameters() *Parameters { return &Parameters{ - Parameters: p2p.DefaultParameters(), + Parameters: shrex.DefaultParameters(), BufferSize: 32 * 1024, } } @@ -36,7 +37,7 @@ func (p *Parameters) Validate() error { } func (c *Client) WithMetrics() error { - metrics, err := p2p.InitClientMetrics("eds") + metrics, err := shrex.InitClientMetrics("eds") if err != nil { return fmt.Errorf("shrex/eds: init Metrics: %w", err) } @@ -45,7 +46,7 @@ func (c *Client) WithMetrics() error { } func (s *Server) WithMetrics() error { - metrics, err := p2p.InitServerMetrics("eds") + metrics, err := shrex.InitServerMetrics("eds") if err != nil { return fmt.Errorf("shrex/eds: init Metrics: %w", err) } diff --git a/share/shwap/p2p/shrex/shrexeds/server.go b/share/shwap/p2p/shrex/shrexeds/server.go new file mode 100644 index 0000000000..6bd0352962 --- /dev/null +++ b/share/shwap/p2p/shrex/shrexeds/server.go @@ -0,0 +1,198 @@ +package shrexeds + +import ( + "context" + "errors" + "fmt" + "io" + "time" + + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/protocol" + "go.uber.org/zap" + + "github.com/celestiaorg/go-libp2p-messenger/serde" + + "github.com/celestiaorg/celestia-node/libs/utils" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" + shrexpb "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/pb" + "github.com/celestiaorg/celestia-node/store" +) + +// Server is responsible for serving ODSs for blocksync over the ShrEx/EDS protocol. +type Server struct { + cancel context.CancelFunc + + host host.Host + protocolID protocol.ID + + store *store.Store + + params *Parameters + // TODO: decouple middleware metrics from shrex and remove middleware from Server + middleware *shrex.Middleware + metrics *shrex.Metrics +} + +// NewServer creates a new ShrEx/EDS server. +func NewServer(params *Parameters, host host.Host, store *store.Store) (*Server, error) { + if err := params.Validate(); err != nil { + return nil, fmt.Errorf("shrex-eds: server creation failed: %w", err) + } + + return &Server{ + host: host, + store: store, + protocolID: shrex.ProtocolID(params.NetworkID(), protocolString), + params: params, + middleware: shrex.NewMiddleware(params.ConcurrencyLimit), + }, nil +} + +func (s *Server) Start(context.Context) error { + ctx, cancel := context.WithCancel(context.Background()) + s.cancel = cancel + + handler := s.streamHandler(ctx) + withRateLimit := s.middleware.RateLimitHandler(handler) + withRecovery := shrex.RecoveryMiddleware(withRateLimit) + s.host.SetStreamHandler(s.protocolID, withRecovery) + return nil +} + +func (s *Server) Stop(context.Context) error { + defer s.cancel() + s.host.RemoveStreamHandler(s.protocolID) + return nil +} + +func (s *Server) observeRateLimitedRequests() { + numRateLimited := s.middleware.DrainCounter() + if numRateLimited > 0 { + s.metrics.ObserveRequests(context.Background(), numRateLimited, shrex.StatusRateLimited) + } +} + +func (s *Server) streamHandler(ctx context.Context) network.StreamHandler { + return func(stream network.Stream) { + err := s.handleEDS(ctx, stream) + if err != nil { + stream.Reset() //nolint:errcheck + return + } + s.metrics.ObserveRequests(ctx, 1, shrex.StatusSuccess) + if err = stream.Close(); err != nil { + log.Debugw("server: closing stream", "err", err) + } + } +} + +func (s *Server) handleEDS(ctx context.Context, stream network.Stream) error { + logger := log.With("peer", stream.Conn().RemotePeer().String()) + logger.Debug("server: handling eds request") + // observe rate limited requests is draining the counter for rate limited requests + // since last handleStream call. This is not optimal observing strategy, but it is + // good enough for now. Will be improved in shrex unification PR. + s.observeRateLimitedRequests() + + // read request from stream to get the dataHash for store lookup + id, err := s.readRequest(logger, stream) + if err != nil { + logger.Warnw("server: reading request from stream", "err", err) + return err + } + + logger = logger.With("height", id.Height) + + ctx, cancel := context.WithTimeout(ctx, s.params.HandleRequestTimeout) + defer cancel() + + // determine whether the EDS is available in our store + // we do not close the reader, so that other requests will not need to re-open the file. + // closing is handled by the LRU cache. + file, err := s.store.GetByHeight(ctx, id.Height) + var status shrexpb.Status + switch { + case err == nil: + defer utils.CloseAndLog(logger, "file", file) + status = shrexpb.Status_OK + case errors.Is(err, store.ErrNotFound): + logger.Warnw("server: request height not found") + s.metrics.ObserveRequests(ctx, 1, shrex.StatusNotFound) + status = shrexpb.Status_NOT_FOUND + case err != nil: + logger.Errorw("server: get file", "err", err) + status = shrexpb.Status_INTERNAL + } + + // inform the client of our status + err = s.writeStatus(logger, status, stream) + if err != nil { + logger.Warnw("server: writing status to stream", "err", err) + return err + } + // if we cannot serve the EDS, we are already done + if status != shrexpb.Status_OK { + return nil + } + + // start streaming the ODS to the client + err = s.writeODS(logger, file, stream) + if err != nil { + logger.Warnw("server: writing ods to stream", "err", err) + return err + } + return nil +} + +func (s *Server) readRequest(logger *zap.SugaredLogger, stream network.Stream) (shwap.EdsID, error) { + err := stream.SetReadDeadline(time.Now().Add(s.params.ServerReadTimeout)) + if err != nil { + logger.Debugw("server: set read deadline", "err", err) + } + + edsID := shwap.EdsID{} + _, err = edsID.ReadFrom(stream) + if err != nil { + return shwap.EdsID{}, fmt.Errorf("reading request: %w", err) + } + err = stream.CloseRead() + if err != nil { + logger.Warnw("server: closing read", "err", err) + } + return edsID, nil +} + +func (s *Server) writeStatus(logger *zap.SugaredLogger, status shrexpb.Status, stream network.Stream) error { + err := stream.SetWriteDeadline(time.Now().Add(s.params.ServerWriteTimeout)) + if err != nil { + logger.Debugw("server: set write deadline", "err", err) + } + + resp := &shrexpb.Response{Status: status} + _, err = serde.Write(stream, resp) + return err +} + +func (s *Server) writeODS(logger *zap.SugaredLogger, streamer eds.Streamer, stream network.Stream) error { + reader, err := streamer.Reader() + if err != nil { + return fmt.Errorf("getting ODS reader: %w", err) + } + err = stream.SetWriteDeadline(time.Now().Add(s.params.ServerWriteTimeout)) + if err != nil { + logger.Debugw("server: set read deadline", "err", err) + } + + buf := make([]byte, s.params.BufferSize) + n, err := io.CopyBuffer(stream, reader, buf) + if err != nil { + return fmt.Errorf("written: %v, writing ODS bytes: %w", n, err) + } + + logger.Debugw("server: wrote ODS", "bytes", n) + return nil +} diff --git a/share/p2p/shrexnd/client.go b/share/shwap/p2p/shrex/shrexnd/client.go similarity index 54% rename from share/p2p/shrexnd/client.go rename to share/shwap/p2p/shrex/shrexnd/client.go index 4fb6c8904b..54d2baaccb 100644 --- a/share/p2p/shrexnd/client.go +++ b/share/shwap/p2p/shrex/shrexnd/client.go @@ -14,11 +14,12 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" "github.com/celestiaorg/go-libp2p-messenger/serde" - "github.com/celestiaorg/nmt" + libshare "github.com/celestiaorg/go-square/v2/share" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/p2p" - pb "github.com/celestiaorg/celestia-node/share/p2p/shrexnd/pb" + "github.com/celestiaorg/celestia-node/libs/utils" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" + shrexpb "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/pb" ) // Client implements client side of shrex/nd protocol to obtain namespaced shares data from remote @@ -28,7 +29,7 @@ type Client struct { protocolID protocol.ID host host.Host - metrics *p2p.Metrics + metrics *shrex.Metrics } // NewClient creates a new shrEx/nd client @@ -39,29 +40,29 @@ func NewClient(params *Parameters, host host.Host) (*Client, error) { return &Client{ host: host, - protocolID: p2p.ProtocolID(params.NetworkID(), protocolString), + protocolID: shrex.ProtocolID(params.NetworkID(), protocolString), params: params, }, nil } // RequestND requests namespaced data from the given peer. -// Returns NamespacedShares with unverified inclusion proofs against the share.Root. +// Returns NamespaceData with unverified inclusion proofs against the share.Root. func (c *Client) RequestND( ctx context.Context, - root *share.Root, - namespace share.Namespace, + height uint64, + namespace libshare.Namespace, peer peer.ID, -) (share.NamespacedShares, error) { +) (shwap.NamespaceData, error) { if err := namespace.ValidateForData(); err != nil { return nil, err } - shares, err := c.doRequest(ctx, root, namespace, peer) + shares, err := c.doRequest(ctx, height, namespace, peer) if err == nil { return shares, nil } if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { - c.metrics.ObserveRequests(ctx, 1, p2p.StatusTimeout) + c.metrics.ObserveRequests(ctx, 1, shrex.StatusTimeout) return nil, err } // some net.Errors also mean the context deadline was exceeded, but yamux/mocknet do not @@ -69,11 +70,11 @@ func (c *Client) RequestND( var ne net.Error if errors.As(err, &ne) && ne.Timeout() { if deadline, _ := ctx.Deadline(); deadline.Before(time.Now()) { - c.metrics.ObserveRequests(ctx, 1, p2p.StatusTimeout) + c.metrics.ObserveRequests(ctx, 1, shrex.StatusTimeout) return nil, context.DeadlineExceeded } } - if !errors.Is(err, p2p.ErrNotFound) && !errors.Is(err, p2p.ErrRateLimited) { + if !errors.Is(err, shrex.ErrNotFound) && errors.Is(err, shrex.ErrRateLimited) { log.Warnw("client-nd: peer returned err", "err", err) } return nil, err @@ -81,51 +82,60 @@ func (c *Client) RequestND( func (c *Client) doRequest( ctx context.Context, - root *share.Root, - namespace share.Namespace, + height uint64, + namespace libshare.Namespace, peerID peer.ID, -) (share.NamespacedShares, error) { - stream, err := c.host.NewStream(ctx, peerID, c.protocolID) +) (shwap.NamespaceData, error) { + streamOpenCtx, cancel := context.WithTimeout(ctx, c.params.ServerReadTimeout) + defer cancel() + stream, err := c.host.NewStream(streamOpenCtx, peerID, c.protocolID) if err != nil { return nil, err } - defer stream.Close() + defer utils.CloseAndLog(log, "client", stream) c.setStreamDeadlines(ctx, stream) - req := &pb.GetSharesByNamespaceRequest{ - RootHash: root.Hash(), - Namespace: namespace, + req, err := shwap.NewNamespaceDataID(height, namespace) + if err != nil { + return nil, fmt.Errorf("client-nd: creating request: %w", err) } - _, err = serde.Write(stream, req) + _, err = req.WriteTo(stream) if err != nil { - c.metrics.ObserveRequests(ctx, 1, p2p.StatusSendReqErr) - stream.Reset() //nolint:errcheck + c.metrics.ObserveRequests(ctx, 1, shrex.StatusSendReqErr) return nil, fmt.Errorf("client-nd: writing request: %w", err) } err = stream.CloseWrite() if err != nil { - log.Debugw("client-nd: closing write side of the stream", "err", err) + log.Warnw("client-nd: closing write side of the stream", "err", err) } if err := c.readStatus(ctx, stream); err != nil { + c.metrics.ObserveRequests(ctx, 1, shrex.StatusReadRespErr) return nil, err } - return c.readNamespacedShares(ctx, stream) + + nd := shwap.NamespaceData{} + _, err = nd.ReadFrom(stream) + if err != nil { + c.metrics.ObserveRequests(ctx, 1, shrex.StatusReadRespErr) + return nil, err + } + return nd, nil } func (c *Client) readStatus(ctx context.Context, stream network.Stream) error { - var resp pb.GetSharesByNamespaceStatusResponse + var resp shrexpb.Response _, err := serde.Read(stream, &resp) if err != nil { // server is overloaded and closed the stream if errors.Is(err, io.EOF) { - c.metrics.ObserveRequests(ctx, 1, p2p.StatusRateLimited) - return p2p.ErrRateLimited + c.metrics.ObserveRequests(ctx, 1, shrex.StatusRateLimited) + return shrex.ErrRateLimited } - c.metrics.ObserveRequests(ctx, 1, p2p.StatusReadRespErr) + c.metrics.ObserveRequests(ctx, 1, shrex.StatusReadRespErr) stream.Reset() //nolint:errcheck return fmt.Errorf("client-nd: reading status response: %w", err) } @@ -133,49 +143,6 @@ func (c *Client) readStatus(ctx context.Context, stream network.Stream) error { return c.convertStatusToErr(ctx, resp.Status) } -// readNamespacedShares converts proto Rows to share.NamespacedShares -func (c *Client) readNamespacedShares( - ctx context.Context, - stream network.Stream, -) (share.NamespacedShares, error) { - var shares share.NamespacedShares - for { - var row pb.NamespaceRowResponse - _, err := serde.Read(stream, &row) - if err != nil { - if errors.Is(err, io.EOF) { - // all data is received and steam is closed by server - return shares, nil - } - c.metrics.ObserveRequests(ctx, 1, p2p.StatusReadRespErr) - return nil, err - } - var proof nmt.Proof - if row.Proof != nil { - if len(row.Shares) != 0 { - proof = nmt.NewInclusionProof( - int(row.Proof.Start), - int(row.Proof.End), - row.Proof.Nodes, - row.Proof.IsMaxNamespaceIgnored, - ) - } else { - proof = nmt.NewAbsenceProof( - int(row.Proof.Start), - int(row.Proof.End), - row.Proof.Nodes, - row.Proof.LeafHash, - row.Proof.IsMaxNamespaceIgnored, - ) - } - } - shares = append(shares, share.NamespacedRow{ - Shares: row.Shares, - Proof: &proof, - }) - } -} - func (c *Client) setStreamDeadlines(ctx context.Context, stream network.Stream) { // set read/write deadline to use context deadline if it exists deadline, ok := ctx.Deadline() @@ -204,20 +171,20 @@ func (c *Client) setStreamDeadlines(ctx context.Context, stream network.Stream) } } -func (c *Client) convertStatusToErr(ctx context.Context, status pb.StatusCode) error { +func (c *Client) convertStatusToErr(ctx context.Context, status shrexpb.Status) error { switch status { - case pb.StatusCode_OK: - c.metrics.ObserveRequests(ctx, 1, p2p.StatusSuccess) + case shrexpb.Status_OK: + c.metrics.ObserveRequests(ctx, 1, shrex.StatusSuccess) return nil - case pb.StatusCode_NOT_FOUND: - c.metrics.ObserveRequests(ctx, 1, p2p.StatusNotFound) - return p2p.ErrNotFound - case pb.StatusCode_INVALID: + case shrexpb.Status_NOT_FOUND: + c.metrics.ObserveRequests(ctx, 1, shrex.StatusNotFound) + return shrex.ErrNotFound + case shrexpb.Status_INVALID: log.Warn("client-nd: invalid request") fallthrough - case pb.StatusCode_INTERNAL: + case shrexpb.Status_INTERNAL: fallthrough default: - return p2p.ErrInvalidResponse + return shrex.ErrInvalidResponse } } diff --git a/share/p2p/shrexnd/doc.go b/share/shwap/p2p/shrex/shrexnd/doc.go similarity index 96% rename from share/p2p/shrexnd/doc.go rename to share/shwap/p2p/shrex/shrexnd/doc.go index 74ba7397e8..63daf6d0b2 100644 --- a/share/p2p/shrexnd/doc.go +++ b/share/shwap/p2p/shrex/shrexnd/doc.go @@ -22,7 +22,7 @@ // // data, err := client.RequestND(ctx, dataRoot, peerID, namespaceID) // -// where data is of type [share.NamespacedShares] +// where data is of type [libshare.NamespacedShares] // // To use a shrexnd server to respond to requests from peers, you must first create a new `shrexnd.Server` instance by: // diff --git a/share/p2p/shrexnd/exchange_test.go b/share/shwap/p2p/shrex/shrexnd/exchange_test.go similarity index 64% rename from share/p2p/shrexnd/exchange_test.go rename to share/shwap/p2p/shrex/shrexnd/exchange_test.go index cb8bbe9d74..450e8c2058 100644 --- a/share/p2p/shrexnd/exchange_test.go +++ b/share/shwap/p2p/shrex/shrexnd/exchange_test.go @@ -3,38 +3,40 @@ package shrexnd import ( "context" "sync" + "sync/atomic" "testing" "time" - "github.com/ipfs/go-datastore" - ds_sync "github.com/ipfs/go-datastore/sync" libhost "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/stretchr/testify/require" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/share/eds" "github.com/celestiaorg/celestia-node/share/eds/edstest" - "github.com/celestiaorg/celestia-node/share/p2p" - "github.com/celestiaorg/celestia-node/share/sharetest" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" + "github.com/celestiaorg/celestia-node/store" ) func TestExchange_RequestND_NotFound(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) t.Cleanup(cancel) edsStore, client, server := makeExchange(t) - require.NoError(t, edsStore.Start(ctx)) require.NoError(t, server.Start(ctx)) + height := atomic.Uint64{} + height.Add(1) + t.Run("CAR_not_exist", func(t *testing.T) { ctx, cancel := context.WithTimeout(ctx, time.Second) t.Cleanup(cancel) - root := share.Root{} - namespace := sharetest.RandV0Namespace() - _, err := client.RequestND(ctx, &root, namespace, server.host.ID()) - require.ErrorIs(t, err, p2p.ErrNotFound) + namespace := libshare.RandomNamespace() + height := height.Add(1) + _, err := client.RequestND(ctx, height, namespace, server.host.ID()) + require.ErrorIs(t, err, shrex.ErrNotFound) }) t.Run("ErrNamespaceNotFound", func(t *testing.T) { @@ -42,12 +44,15 @@ func TestExchange_RequestND_NotFound(t *testing.T) { t.Cleanup(cancel) eds := edstest.RandEDS(t, 4) - dah, err := share.NewRoot(eds) + roots, err := share.NewAxisRoots(eds) require.NoError(t, err) - require.NoError(t, edsStore.Put(ctx, dah.Hash(), eds)) - namespace := sharetest.RandV0Namespace() - emptyShares, err := client.RequestND(ctx, dah, namespace, server.host.ID()) + height := height.Add(1) + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + namespace := libshare.RandomNamespace() + emptyShares, err := client.RequestND(ctx, height, namespace, server.host.ID()) require.NoError(t, err) require.Empty(t, emptyShares.Flatten()) }) @@ -83,34 +88,24 @@ func TestExchange_RequestND(t *testing.T) { t.Fatal("timeout") } } - middleware := p2p.NewMiddleware(rateLimit) + middleware := shrex.NewMiddleware(rateLimit) server.host.SetStreamHandler(server.protocolID, middleware.RateLimitHandler(mockHandler)) // take server concurrency slots with blocked requests for i := 0; i < rateLimit; i++ { go func(i int) { - client.RequestND(ctx, nil, sharetest.RandV0Namespace(), server.host.ID()) //nolint:errcheck + client.RequestND(ctx, 1, libshare.RandomNamespace(), server.host.ID()) //nolint:errcheck }(i) } // wait until all server slots are taken wg.Wait() - _, err = client.RequestND(ctx, nil, sharetest.RandV0Namespace(), server.host.ID()) - require.ErrorIs(t, err, p2p.ErrRateLimited) + _, err = client.RequestND(ctx, 1, libshare.RandomNamespace(), server.host.ID()) + require.ErrorIs(t, err, shrex.ErrRateLimited) }) } -func newStore(t *testing.T) *eds.Store { - t.Helper() - - storeCfg := eds.DefaultParameters() - ds := ds_sync.MutexWrap(datastore.NewMapDatastore()) - store, err := eds.NewStore(storeCfg, t.TempDir(), ds) - require.NoError(t, err) - return store -} - func createMocknet(t *testing.T, amount int) []libhost.Host { t.Helper() @@ -120,15 +115,16 @@ func createMocknet(t *testing.T, amount int) []libhost.Host { return net.Hosts() } -func makeExchange(t *testing.T) (*eds.Store, *Client, *Server) { +func makeExchange(t *testing.T) (*store.Store, *Client, *Server) { t.Helper() - store := newStore(t) + s, err := store.NewStore(store.DefaultParameters(), t.TempDir()) + require.NoError(t, err) hosts := createMocknet(t, 2) client, err := NewClient(DefaultParameters(), hosts[0]) require.NoError(t, err) - server, err := NewServer(DefaultParameters(), hosts[1], store) + server, err := NewServer(DefaultParameters(), hosts[1], s) require.NoError(t, err) - return store, client, server + return s, client, server } diff --git a/share/p2p/shrexnd/params.go b/share/shwap/p2p/shrex/shrexnd/params.go similarity index 61% rename from share/p2p/shrexnd/params.go rename to share/shwap/p2p/shrex/shrexnd/params.go index 8489627a07..5544ae6b27 100644 --- a/share/p2p/shrexnd/params.go +++ b/share/shwap/p2p/shrex/shrexnd/params.go @@ -5,22 +5,23 @@ import ( logging "github.com/ipfs/go-log/v2" - "github.com/celestiaorg/celestia-node/share/p2p" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" ) -const protocolString = "/shrex/nd/v0.0.3" +const protocolString = shrex.ProtocolString + shwap.NamespaceDataName var log = logging.Logger("shrex/nd") // Parameters is the set of parameters that must be configured for the shrex/eds protocol. -type Parameters = p2p.Parameters +type Parameters = shrex.Parameters func DefaultParameters() *Parameters { - return p2p.DefaultParameters() + return shrex.DefaultParameters() } func (c *Client) WithMetrics() error { - metrics, err := p2p.InitClientMetrics("nd") + metrics, err := shrex.InitClientMetrics("nd") if err != nil { return fmt.Errorf("shrex/nd: init Metrics: %w", err) } @@ -29,7 +30,7 @@ func (c *Client) WithMetrics() error { } func (srv *Server) WithMetrics() error { - metrics, err := p2p.InitServerMetrics("nd") + metrics, err := shrex.InitServerMetrics("nd") if err != nil { return fmt.Errorf("shrex/nd: init Metrics: %w", err) } diff --git a/share/shwap/p2p/shrex/shrexnd/server.go b/share/shwap/p2p/shrex/shrexnd/server.go new file mode 100644 index 0000000000..b6563fa3a0 --- /dev/null +++ b/share/shwap/p2p/shrex/shrexnd/server.go @@ -0,0 +1,226 @@ +package shrexnd + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/protocol" + "go.uber.org/zap" + + "github.com/celestiaorg/go-libp2p-messenger/serde" + + "github.com/celestiaorg/celestia-node/libs/utils" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" + "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex" + shrexpb "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/pb" + "github.com/celestiaorg/celestia-node/store" +) + +// Server implements server side of shrex/nd protocol to serve namespaced share to remote +// peers. +type Server struct { + cancel context.CancelFunc + + host host.Host + protocolID protocol.ID + + handler network.StreamHandler + store *store.Store + + params *Parameters + // TODO: decouple middleware metrics from shrex and remove middleware from Server + middleware *shrex.Middleware + metrics *shrex.Metrics +} + +// NewServer creates new Server +func NewServer(params *Parameters, host host.Host, store *store.Store) (*Server, error) { + if err := params.Validate(); err != nil { + return nil, fmt.Errorf("shrex-nd: server creation failed: %w", err) + } + + srv := &Server{ + store: store, + host: host, + params: params, + protocolID: shrex.ProtocolID(params.NetworkID(), protocolString), + middleware: shrex.NewMiddleware(params.ConcurrencyLimit), + } + + ctx, cancel := context.WithCancel(context.Background()) + srv.cancel = cancel + + handler := srv.streamHandler(ctx) + withRateLimit := srv.middleware.RateLimitHandler(handler) + withRecovery := shrex.RecoveryMiddleware(withRateLimit) + srv.handler = withRecovery + return srv, nil +} + +// Start starts the server +func (srv *Server) Start(context.Context) error { + srv.host.SetStreamHandler(srv.protocolID, srv.handler) + return nil +} + +// Stop stops the server +func (srv *Server) Stop(context.Context) error { + srv.cancel() + srv.host.RemoveStreamHandler(srv.protocolID) + return nil +} + +func (srv *Server) streamHandler(ctx context.Context) network.StreamHandler { + return func(s network.Stream) { + err := srv.handleNamespaceData(ctx, s) + if err != nil { + s.Reset() //nolint:errcheck + return + } + if err = s.Close(); err != nil { + log.Debugw("server: closing stream", "err", err) + } + } +} + +// SetHandler sets server handler +func (srv *Server) SetHandler(handler network.StreamHandler) { + srv.handler = handler +} + +func (srv *Server) observeRateLimitedRequests() { + numRateLimited := srv.middleware.DrainCounter() + if numRateLimited > 0 { + srv.metrics.ObserveRequests(context.Background(), numRateLimited, shrex.StatusRateLimited) + } +} + +func (srv *Server) handleNamespaceData(ctx context.Context, stream network.Stream) error { + logger := log.With("source", "server", "peer", stream.Conn().RemotePeer().String()) + logger.Debug("handling nd request") + + srv.observeRateLimitedRequests() + ndid, err := srv.readRequest(logger, stream) + if err != nil { + logger.Warnw("read request", "err", err) + srv.metrics.ObserveRequests(ctx, 1, shrex.StatusBadRequest) + return err + } + + logger = logger.With( + "namespace", ndid.DataNamespace.String(), + "height", ndid.Height, + ) + logger.Debugw("new request") + + ctx, cancel := context.WithTimeout(ctx, srv.params.HandleRequestTimeout) + defer cancel() + + nd, status, err := srv.getNamespaceData(ctx, ndid) + if err != nil { + // server should respond with status regardless if there was an error getting data + sendErr := srv.respondStatus(ctx, logger, stream, status) + if sendErr != nil { + logger.Errorw("sending response", "err", sendErr) + srv.metrics.ObserveRequests(ctx, 1, shrex.StatusSendRespErr) + } + logger.Errorw("handling request", "err", err) + return errors.Join(err, sendErr) + } + + err = srv.respondStatus(ctx, logger, stream, status) + if err != nil { + logger.Errorw("sending response", "err", err) + srv.metrics.ObserveRequests(ctx, 1, shrex.StatusSendRespErr) + return err + } + + _, err = nd.WriteTo(stream) + if err != nil { + logger.Errorw("send nd data", "err", err) + srv.metrics.ObserveRequests(ctx, 1, shrex.StatusSendRespErr) + return err + } + return nil +} + +func (srv *Server) readRequest( + logger *zap.SugaredLogger, + stream network.Stream, +) (shwap.NamespaceDataID, error) { + err := stream.SetReadDeadline(time.Now().Add(srv.params.ServerReadTimeout)) + if err != nil { + logger.Debugw("setting read deadline", "err", err) + } + + ndid := shwap.NamespaceDataID{} + _, err = ndid.ReadFrom(stream) + if err != nil { + return shwap.NamespaceDataID{}, fmt.Errorf("reading request: %w", err) + } + + err = stream.CloseRead() + if err != nil { + logger.Warnw("closing read side of the stream", "err", err) + } + + return ndid, nil +} + +func (srv *Server) getNamespaceData( + ctx context.Context, + id shwap.NamespaceDataID, +) (shwap.NamespaceData, shrexpb.Status, error) { + file, err := srv.store.GetByHeight(ctx, id.Height) + if errors.Is(err, store.ErrNotFound) { + return nil, shrexpb.Status_NOT_FOUND, nil + } + if err != nil { + return nil, shrexpb.Status_INTERNAL, fmt.Errorf("retrieving DAH: %w", err) + } + defer utils.CloseAndLog(log, "file", file) + + nd, err := eds.NamespaceData(ctx, file, id.DataNamespace) + if err != nil { + return nil, shrexpb.Status_INVALID, fmt.Errorf("getting nd: %w", err) + } + + return nd, shrexpb.Status_OK, nil +} + +func (srv *Server) respondStatus( + ctx context.Context, + logger *zap.SugaredLogger, + stream network.Stream, + status shrexpb.Status, +) error { + srv.observeStatus(ctx, status) + + err := stream.SetWriteDeadline(time.Now().Add(srv.params.ServerWriteTimeout)) + if err != nil { + logger.Debugw("setting write deadline", "err", err) + } + + _, err = serde.Write(stream, &shrexpb.Response{Status: status}) + if err != nil { + return fmt.Errorf("writing response: %w", err) + } + + return nil +} + +func (srv *Server) observeStatus(ctx context.Context, status shrexpb.Status) { + switch { + case status == shrexpb.Status_OK: + srv.metrics.ObserveRequests(ctx, 1, shrex.StatusSuccess) + case status != shrexpb.Status_NOT_FOUND: + srv.metrics.ObserveRequests(ctx, 1, shrex.StatusNotFound) + case status == shrexpb.Status_INVALID: + srv.metrics.ObserveRequests(ctx, 1, shrex.StatusInternalErr) + } +} diff --git a/share/p2p/shrexsub/doc.go b/share/shwap/p2p/shrex/shrexsub/doc.go similarity index 100% rename from share/p2p/shrexsub/doc.go rename to share/shwap/p2p/shrex/shrexsub/doc.go diff --git a/share/p2p/shrexsub/pb/notification.pb.go b/share/shwap/p2p/shrex/shrexsub/pb/notification.pb.go similarity index 84% rename from share/p2p/shrexsub/pb/notification.pb.go rename to share/shwap/p2p/shrex/shrexsub/pb/notification.pb.go index e154dc62b7..6102251ff1 100644 --- a/share/p2p/shrexsub/pb/notification.pb.go +++ b/share/shwap/p2p/shrex/shrexsub/pb/notification.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: share/p2p/shrexsub/pb/notification.proto +// source: share/shwap/p2p/shrex/shrexsub/pb/notification.proto -package share_p2p_shrex_sub +package libshare_p2p_shrex_sub import ( fmt "fmt" @@ -31,7 +31,7 @@ func (m *RecentEDSNotification) Reset() { *m = RecentEDSNotification{} } func (m *RecentEDSNotification) String() string { return proto.CompactTextString(m) } func (*RecentEDSNotification) ProtoMessage() {} func (*RecentEDSNotification) Descriptor() ([]byte, []int) { - return fileDescriptor_1a6ade914b560e62, []int{0} + return fileDescriptor_c16b670e7e556100, []int{0} } func (m *RecentEDSNotification) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -79,22 +79,23 @@ func init() { } func init() { - proto.RegisterFile("share/p2p/shrexsub/pb/notification.proto", fileDescriptor_1a6ade914b560e62) + proto.RegisterFile("share/shwap/p2p/shrex/shrexsub/pb/notification.proto", fileDescriptor_c16b670e7e556100) } -var fileDescriptor_1a6ade914b560e62 = []byte{ - // 176 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x28, 0xce, 0x48, 0x2c, - 0x4a, 0xd5, 0x2f, 0x30, 0x2a, 0xd0, 0x2f, 0xce, 0x28, 0x4a, 0xad, 0x28, 0x2e, 0x4d, 0xd2, 0x2f, - 0x48, 0xd2, 0xcf, 0xcb, 0x2f, 0xc9, 0x4c, 0xcb, 0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x2b, - 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x06, 0xab, 0xd4, 0x2b, 0x30, 0x2a, 0xd0, 0x03, 0xab, 0xd4, - 0x2b, 0x2e, 0x4d, 0x52, 0xf2, 0xe1, 0x12, 0x0d, 0x4a, 0x4d, 0x4e, 0xcd, 0x2b, 0x71, 0x75, 0x09, - 0xf6, 0x43, 0xd2, 0x23, 0x24, 0xc6, 0xc5, 0x96, 0x91, 0x9a, 0x99, 0x9e, 0x51, 0x22, 0xc1, 0xa8, - 0xc0, 0xa8, 0xc1, 0x12, 0x04, 0xe5, 0x09, 0x49, 0x73, 0x71, 0xa6, 0x24, 0x96, 0x24, 0xc6, 0x67, - 0x24, 0x16, 0x67, 0x48, 0x30, 0x29, 0x30, 0x6a, 0xf0, 0x04, 0x71, 0x80, 0x04, 0x3c, 0x12, 0x8b, - 0x33, 0x9c, 0x24, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, - 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x89, 0x0d, 0xec, - 0x06, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0x16, 0xea, 0xc6, 0xaf, 0x00, 0x00, 0x00, +var fileDescriptor_c16b670e7e556100 = []byte{ + // 189 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x29, 0xce, 0x48, 0x2c, + 0x4a, 0xd5, 0x2f, 0xce, 0x28, 0x4f, 0x2c, 0xd0, 0x2f, 0x30, 0x2a, 0xd0, 0x2f, 0xce, 0x28, 0x4a, + 0xad, 0x80, 0x90, 0xc5, 0xa5, 0x49, 0xfa, 0x05, 0x49, 0xfa, 0x79, 0xf9, 0x25, 0x99, 0x69, 0x99, + 0xc9, 0x89, 0x25, 0x99, 0xf9, 0x79, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x62, 0xe9, 0xf9, + 0xc5, 0x85, 0xa5, 0x89, 0x45, 0xa9, 0x7a, 0x05, 0x46, 0x05, 0x7a, 0x60, 0xc5, 0x7a, 0xc5, 0xa5, + 0x49, 0x4a, 0x3e, 0x5c, 0xa2, 0x41, 0xa9, 0xc9, 0xa9, 0x79, 0x25, 0xae, 0x2e, 0xc1, 0x7e, 0x48, + 0xda, 0x84, 0xc4, 0xb8, 0xd8, 0x32, 0x52, 0x33, 0xd3, 0x33, 0x4a, 0x24, 0x18, 0x15, 0x18, 0x35, + 0x58, 0x82, 0xa0, 0x3c, 0x21, 0x69, 0x2e, 0xce, 0x94, 0xc4, 0x92, 0xc4, 0xf8, 0x8c, 0xc4, 0xe2, + 0x0c, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x0e, 0x90, 0x80, 0x47, 0x62, 0x71, 0x86, 0x93, + 0xc4, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, + 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x24, 0xb1, 0x81, 0x9d, 0x61, 0x0c, + 0x08, 0x00, 0x00, 0xff, 0xff, 0xa9, 0x5f, 0xae, 0x6a, 0xbe, 0x00, 0x00, 0x00, } func (m *RecentEDSNotification) Marshal() (dAtA []byte, err error) { diff --git a/share/p2p/shrexsub/pb/notification.proto b/share/shwap/p2p/shrex/shrexsub/pb/notification.proto similarity index 100% rename from share/p2p/shrexsub/pb/notification.proto rename to share/shwap/p2p/shrex/shrexsub/pb/notification.proto diff --git a/share/p2p/shrexsub/pubsub.go b/share/shwap/p2p/shrex/shrexsub/pubsub.go similarity index 93% rename from share/p2p/shrexsub/pubsub.go rename to share/shwap/p2p/shrex/shrexsub/pubsub.go index 774fb436fa..d1861cfe12 100644 --- a/share/p2p/shrexsub/pubsub.go +++ b/share/shwap/p2p/shrex/shrexsub/pubsub.go @@ -10,14 +10,14 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/celestiaorg/celestia-node/share" - pb "github.com/celestiaorg/celestia-node/share/p2p/shrexsub/pb" + pb "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub/pb" ) var log = logging.Logger("shrex-sub") // pubsubTopic hardcodes the name of the EDS floodsub topic with the provided networkID. func pubsubTopicID(networkID string) string { - return fmt.Sprintf("%s/eds-sub/v0.1.0", networkID) + return fmt.Sprintf("%s/eds-sub/v0.2.0", networkID) } // ValidatorFn is an injectable func and governs EDS notification msg validity. @@ -111,7 +111,7 @@ func (v ValidatorFn) validate(ctx context.Context, p peer.ID, msg *pubsub.Messag DataHash: pbmsg.DataHash, Height: pbmsg.Height, } - if n.Height == 0 || n.DataHash.IsEmptyRoot() || n.DataHash.Validate() != nil { + if n.Height == 0 || n.DataHash.IsEmptyEDS() || n.DataHash.Validate() != nil { // hard reject malicious height (height 0 does not exist) and // empty/invalid datahashes return pubsub.ValidationReject @@ -129,7 +129,7 @@ func (s *PubSub) Subscribe() (*Subscription, error) { // Broadcast sends the EDS notification (DataHash) to every connected peer. func (s *PubSub) Broadcast(ctx context.Context, notification Notification) error { - if notification.DataHash.IsEmptyRoot() { + if notification.DataHash.IsEmptyEDS() { // no need to broadcast datahash of an empty block EDS return nil } diff --git a/share/p2p/shrexsub/pubsub_test.go b/share/shwap/p2p/shrex/shrexsub/pubsub_test.go similarity index 97% rename from share/p2p/shrexsub/pubsub_test.go rename to share/shwap/p2p/shrex/shrexsub/pubsub_test.go index 5938a414a2..59602da29b 100644 --- a/share/p2p/shrexsub/pubsub_test.go +++ b/share/shwap/p2p/shrex/shrexsub/pubsub_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/rand" - pb "github.com/celestiaorg/celestia-node/share/p2p/shrexsub/pb" + pb "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub/pb" ) func TestPubSub(t *testing.T) { diff --git a/share/p2p/shrexsub/subscription.go b/share/shwap/p2p/shrex/shrexsub/subscription.go similarity index 94% rename from share/p2p/shrexsub/subscription.go rename to share/shwap/p2p/shrex/shrexsub/subscription.go index 32a3e65e51..5021f090c2 100644 --- a/share/p2p/shrexsub/subscription.go +++ b/share/shwap/p2p/shrex/shrexsub/subscription.go @@ -6,7 +6,7 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" - pb "github.com/celestiaorg/celestia-node/share/p2p/shrexsub/pb" + pb "github.com/celestiaorg/celestia-node/share/shwap/p2p/shrex/shrexsub/pb" ) // Subscription is a wrapper over pubsub.Subscription that handles diff --git a/share/shwap/pb/shwap.pb.go b/share/shwap/pb/shwap.pb.go new file mode 100644 index 0000000000..000bf78ca7 --- /dev/null +++ b/share/shwap/pb/shwap.pb.go @@ -0,0 +1,1114 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: share/shwap/pb/shwap.proto + +package pb + +import ( + fmt "fmt" + pb "github.com/celestiaorg/nmt/pb" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type AxisType int32 + +const ( + AxisType_ROW AxisType = 0 + AxisType_COL AxisType = 1 +) + +var AxisType_name = map[int32]string{ + 0: "ROW", + 1: "COL", +} + +var AxisType_value = map[string]int32{ + "ROW": 0, + "COL": 1, +} + +func (x AxisType) String() string { + return proto.EnumName(AxisType_name, int32(x)) +} + +func (AxisType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_9431653f3c9f0bcb, []int{0} +} + +type Row_HalfSide int32 + +const ( + Row_LEFT Row_HalfSide = 0 + Row_RIGHT Row_HalfSide = 1 +) + +var Row_HalfSide_name = map[int32]string{ + 0: "LEFT", + 1: "RIGHT", +} + +var Row_HalfSide_value = map[string]int32{ + "LEFT": 0, + "RIGHT": 1, +} + +func (x Row_HalfSide) String() string { + return proto.EnumName(Row_HalfSide_name, int32(x)) +} + +func (Row_HalfSide) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_9431653f3c9f0bcb, []int{0, 0} +} + +type Row struct { + SharesHalf []*Share `protobuf:"bytes,1,rep,name=shares_half,json=sharesHalf,proto3" json:"shares_half,omitempty"` + HalfSide Row_HalfSide `protobuf:"varint,2,opt,name=half_side,json=halfSide,proto3,enum=shwap.Row_HalfSide" json:"half_side,omitempty"` +} + +func (m *Row) Reset() { *m = Row{} } +func (m *Row) String() string { return proto.CompactTextString(m) } +func (*Row) ProtoMessage() {} +func (*Row) Descriptor() ([]byte, []int) { + return fileDescriptor_9431653f3c9f0bcb, []int{0} +} +func (m *Row) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Row) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Row.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Row) XXX_Merge(src proto.Message) { + xxx_messageInfo_Row.Merge(m, src) +} +func (m *Row) XXX_Size() int { + return m.Size() +} +func (m *Row) XXX_DiscardUnknown() { + xxx_messageInfo_Row.DiscardUnknown(m) +} + +var xxx_messageInfo_Row proto.InternalMessageInfo + +func (m *Row) GetSharesHalf() []*Share { + if m != nil { + return m.SharesHalf + } + return nil +} + +func (m *Row) GetHalfSide() Row_HalfSide { + if m != nil { + return m.HalfSide + } + return Row_LEFT +} + +type Sample struct { + Share *Share `protobuf:"bytes,1,opt,name=share,proto3" json:"share,omitempty"` + Proof *pb.Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + ProofType AxisType `protobuf:"varint,3,opt,name=proof_type,json=proofType,proto3,enum=shwap.AxisType" json:"proof_type,omitempty"` +} + +func (m *Sample) Reset() { *m = Sample{} } +func (m *Sample) String() string { return proto.CompactTextString(m) } +func (*Sample) ProtoMessage() {} +func (*Sample) Descriptor() ([]byte, []int) { + return fileDescriptor_9431653f3c9f0bcb, []int{1} +} +func (m *Sample) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Sample) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Sample.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Sample) XXX_Merge(src proto.Message) { + xxx_messageInfo_Sample.Merge(m, src) +} +func (m *Sample) XXX_Size() int { + return m.Size() +} +func (m *Sample) XXX_DiscardUnknown() { + xxx_messageInfo_Sample.DiscardUnknown(m) +} + +var xxx_messageInfo_Sample proto.InternalMessageInfo + +func (m *Sample) GetShare() *Share { + if m != nil { + return m.Share + } + return nil +} + +func (m *Sample) GetProof() *pb.Proof { + if m != nil { + return m.Proof + } + return nil +} + +func (m *Sample) GetProofType() AxisType { + if m != nil { + return m.ProofType + } + return AxisType_ROW +} + +type RowNamespaceData struct { + Shares []*Share `protobuf:"bytes,1,rep,name=shares,proto3" json:"shares,omitempty"` + Proof *pb.Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` +} + +func (m *RowNamespaceData) Reset() { *m = RowNamespaceData{} } +func (m *RowNamespaceData) String() string { return proto.CompactTextString(m) } +func (*RowNamespaceData) ProtoMessage() {} +func (*RowNamespaceData) Descriptor() ([]byte, []int) { + return fileDescriptor_9431653f3c9f0bcb, []int{2} +} +func (m *RowNamespaceData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RowNamespaceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RowNamespaceData.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RowNamespaceData) XXX_Merge(src proto.Message) { + xxx_messageInfo_RowNamespaceData.Merge(m, src) +} +func (m *RowNamespaceData) XXX_Size() int { + return m.Size() +} +func (m *RowNamespaceData) XXX_DiscardUnknown() { + xxx_messageInfo_RowNamespaceData.DiscardUnknown(m) +} + +var xxx_messageInfo_RowNamespaceData proto.InternalMessageInfo + +func (m *RowNamespaceData) GetShares() []*Share { + if m != nil { + return m.Shares + } + return nil +} + +func (m *RowNamespaceData) GetProof() *pb.Proof { + if m != nil { + return m.Proof + } + return nil +} + +type Share struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *Share) Reset() { *m = Share{} } +func (m *Share) String() string { return proto.CompactTextString(m) } +func (*Share) ProtoMessage() {} +func (*Share) Descriptor() ([]byte, []int) { + return fileDescriptor_9431653f3c9f0bcb, []int{3} +} +func (m *Share) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Share) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Share.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Share) XXX_Merge(src proto.Message) { + xxx_messageInfo_Share.Merge(m, src) +} +func (m *Share) XXX_Size() int { + return m.Size() +} +func (m *Share) XXX_DiscardUnknown() { + xxx_messageInfo_Share.DiscardUnknown(m) +} + +var xxx_messageInfo_Share proto.InternalMessageInfo + +func (m *Share) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func init() { + proto.RegisterEnum("shwap.AxisType", AxisType_name, AxisType_value) + proto.RegisterEnum("shwap.Row_HalfSide", Row_HalfSide_name, Row_HalfSide_value) + proto.RegisterType((*Row)(nil), "shwap.Row") + proto.RegisterType((*Sample)(nil), "shwap.Sample") + proto.RegisterType((*RowNamespaceData)(nil), "shwap.RowNamespaceData") + proto.RegisterType((*Share)(nil), "shwap.Share") +} + +func init() { proto.RegisterFile("share/shwap/pb/shwap.proto", fileDescriptor_9431653f3c9f0bcb) } + +var fileDescriptor_9431653f3c9f0bcb = []byte{ + // 381 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x6b, 0xe2, 0x40, + 0x18, 0xc6, 0x33, 0x1b, 0xe3, 0xc6, 0x57, 0xd1, 0x30, 0x7b, 0x09, 0xee, 0x92, 0x95, 0xb0, 0x0b, + 0xb2, 0x60, 0xb2, 0xe8, 0x27, 0xd8, 0xbf, 0xb5, 0x60, 0x6b, 0x19, 0x85, 0x42, 0x2f, 0x61, 0x62, + 0x46, 0x13, 0x88, 0x9d, 0x21, 0x49, 0x49, 0x3d, 0xf7, 0xd0, 0x6b, 0x3f, 0x56, 0x8f, 0x1e, 0x7b, + 0x2c, 0xfa, 0x45, 0x4a, 0x26, 0xb1, 0x14, 0xda, 0x43, 0x6f, 0xbf, 0xcc, 0xf3, 0xcc, 0xbc, 0xcf, + 0x13, 0x5e, 0xe8, 0xa6, 0x21, 0x4d, 0x98, 0x9b, 0x86, 0x39, 0x15, 0xae, 0xf0, 0x4b, 0x70, 0x44, + 0xc2, 0x33, 0x8e, 0x35, 0xf9, 0xd1, 0x6d, 0x0b, 0xdf, 0x15, 0x09, 0xe7, 0xcb, 0xf2, 0xd8, 0xbe, + 0x45, 0xa0, 0x12, 0x9e, 0xe3, 0x01, 0x34, 0xe5, 0xe5, 0xd4, 0x0b, 0x69, 0xbc, 0x34, 0x51, 0x4f, + 0xed, 0x37, 0x87, 0x2d, 0xa7, 0x7c, 0x61, 0x56, 0x28, 0x04, 0x4a, 0xc3, 0x98, 0xc6, 0x4b, 0xfc, + 0x13, 0x1a, 0x85, 0xcf, 0x4b, 0xa3, 0x80, 0x99, 0x1f, 0x7a, 0xa8, 0xdf, 0x1e, 0x7e, 0xaa, 0xcc, + 0x84, 0xe7, 0x4e, 0xe1, 0x99, 0x45, 0x01, 0x23, 0x7a, 0x58, 0x91, 0xfd, 0x15, 0xf4, 0xc3, 0x29, + 0xd6, 0xa1, 0x36, 0xf9, 0xf7, 0x7f, 0x6e, 0x28, 0xb8, 0x01, 0x1a, 0x39, 0x3e, 0x1a, 0xcf, 0x0d, + 0x64, 0xdf, 0x20, 0xa8, 0xcf, 0xe8, 0x5a, 0xc4, 0x0c, 0xdb, 0xa0, 0xc9, 0x59, 0x26, 0xea, 0xa1, + 0x57, 0x31, 0x4a, 0x09, 0x7f, 0x07, 0x4d, 0xf6, 0x90, 0xd3, 0x9b, 0xc3, 0x8e, 0x53, 0xb5, 0xf2, + 0x9d, 0xb3, 0x02, 0x48, 0xa9, 0x62, 0x07, 0x40, 0x82, 0x97, 0x6d, 0x04, 0x33, 0x55, 0x99, 0xb4, + 0x53, 0xbd, 0xf7, 0xeb, 0x3a, 0x4a, 0xe7, 0x1b, 0xc1, 0x48, 0x43, 0x5a, 0x0a, 0xb4, 0x3d, 0x30, + 0x08, 0xcf, 0x4f, 0xe9, 0x9a, 0xa5, 0x82, 0x2e, 0xd8, 0x5f, 0x9a, 0x51, 0xfc, 0x0d, 0xea, 0x65, + 0xf5, 0x37, 0x7f, 0x4b, 0xa5, 0xbd, 0x33, 0x90, 0xfd, 0x19, 0x34, 0x79, 0x0f, 0x63, 0xa8, 0x05, + 0x34, 0xa3, 0xb2, 0x63, 0x8b, 0x48, 0xfe, 0xf1, 0x05, 0xf4, 0x43, 0x28, 0xfc, 0x11, 0x54, 0x32, + 0x3d, 0x37, 0x94, 0x02, 0xfe, 0x4c, 0x27, 0x06, 0xfa, 0x7d, 0x72, 0xbf, 0xb3, 0xd0, 0x76, 0x67, + 0xa1, 0xc7, 0x9d, 0x85, 0xee, 0xf6, 0x96, 0xb2, 0xdd, 0x5b, 0xca, 0xc3, 0xde, 0x52, 0x2e, 0x46, + 0xab, 0x28, 0x0b, 0xaf, 0x7c, 0x67, 0xc1, 0xd7, 0xee, 0x82, 0xc5, 0x2c, 0xcd, 0x22, 0xca, 0x93, + 0xd5, 0x33, 0x0f, 0x2e, 0x79, 0x50, 0xec, 0xc5, 0xcb, 0xed, 0xf0, 0xeb, 0x72, 0x03, 0x46, 0x4f, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x67, 0xb6, 0xc0, 0x8b, 0x36, 0x02, 0x00, 0x00, +} + +func (m *Row) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Row) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Row) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.HalfSide != 0 { + i = encodeVarintShwap(dAtA, i, uint64(m.HalfSide)) + i-- + dAtA[i] = 0x10 + } + if len(m.SharesHalf) > 0 { + for iNdEx := len(m.SharesHalf) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SharesHalf[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShwap(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Sample) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Sample) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Sample) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ProofType != 0 { + i = encodeVarintShwap(dAtA, i, uint64(m.ProofType)) + i-- + dAtA[i] = 0x18 + } + if m.Proof != nil { + { + size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShwap(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Share != nil { + { + size, err := m.Share.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShwap(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RowNamespaceData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RowNamespaceData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RowNamespaceData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Proof != nil { + { + size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShwap(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Shares) > 0 { + for iNdEx := len(m.Shares) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Shares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShwap(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Share) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Share) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Share) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintShwap(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintShwap(dAtA []byte, offset int, v uint64) int { + offset -= sovShwap(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Row) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SharesHalf) > 0 { + for _, e := range m.SharesHalf { + l = e.Size() + n += 1 + l + sovShwap(uint64(l)) + } + } + if m.HalfSide != 0 { + n += 1 + sovShwap(uint64(m.HalfSide)) + } + return n +} + +func (m *Sample) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Share != nil { + l = m.Share.Size() + n += 1 + l + sovShwap(uint64(l)) + } + if m.Proof != nil { + l = m.Proof.Size() + n += 1 + l + sovShwap(uint64(l)) + } + if m.ProofType != 0 { + n += 1 + sovShwap(uint64(m.ProofType)) + } + return n +} + +func (m *RowNamespaceData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Shares) > 0 { + for _, e := range m.Shares { + l = e.Size() + n += 1 + l + sovShwap(uint64(l)) + } + } + if m.Proof != nil { + l = m.Proof.Size() + n += 1 + l + sovShwap(uint64(l)) + } + return n +} + +func (m *Share) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovShwap(uint64(l)) + } + return n +} + +func sovShwap(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozShwap(x uint64) (n int) { + return sovShwap(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Row) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Row: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Row: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SharesHalf", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShwap + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SharesHalf = append(m.SharesHalf, &Share{}) + if err := m.SharesHalf[len(m.SharesHalf)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HalfSide", wireType) + } + m.HalfSide = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HalfSide |= Row_HalfSide(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipShwap(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthShwap + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Sample) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Sample: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Sample: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Share", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShwap + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Share == nil { + m.Share = &Share{} + } + if err := m.Share.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShwap + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Proof == nil { + m.Proof = &pb.Proof{} + } + if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofType", wireType) + } + m.ProofType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProofType |= AxisType(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipShwap(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthShwap + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RowNamespaceData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RowNamespaceData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RowNamespaceData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShwap + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shares = append(m.Shares, &Share{}) + if err := m.Shares[len(m.Shares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShwap + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Proof == nil { + m.Proof = &pb.Proof{} + } + if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShwap(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthShwap + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Share) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Share: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Share: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShwap + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthShwap + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthShwap + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShwap(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthShwap + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipShwap(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShwap + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShwap + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShwap + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthShwap + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupShwap + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthShwap + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthShwap = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowShwap = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupShwap = fmt.Errorf("proto: unexpected end of group") +) diff --git a/share/shwap/pb/shwap.proto b/share/shwap/pb/shwap.proto new file mode 100644 index 0000000000..d7daea568a --- /dev/null +++ b/share/shwap/pb/shwap.proto @@ -0,0 +1,36 @@ +// Defined in CIP-19 https://github.com/celestiaorg/CIPs/blob/82aeb7dfc472105a11babffd548c730c899a3d24/cips/cip-19.md +syntax = "proto3"; +package shwap; +option go_package = "github.com/celestiaorg/celestia-node/share/shwap/pb"; + +import "pb/proof.proto"; // celestiaorg/nmt/pb/proof.proto + +message Row { + repeated Share shares_half = 1; + HalfSide half_side= 2; + + enum HalfSide { + LEFT = 0; + RIGHT = 1; + } +} + +message Sample { + Share share = 1; + proof.pb.Proof proof = 2; + AxisType proof_type = 3; +} + +message RowNamespaceData { + repeated Share shares = 1; + proof.pb.Proof proof = 2; +} + +message Share { + bytes data = 1; +} + +enum AxisType { + ROW = 0; + COL = 1; +} diff --git a/share/shwap/row.go b/share/shwap/row.go new file mode 100644 index 0000000000..87faff4875 --- /dev/null +++ b/share/shwap/row.go @@ -0,0 +1,242 @@ +package shwap + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap/pb" +) + +// RowName is the name identifier for the row container. +const RowName = "row_v0" + +// RowSide enumerates the possible sides of a row within an Extended Data Square (EDS). +type RowSide int + +const ( + Left RowSide = iota // Left side of the row. + Right // Right side of the row. + Both // Both sides of the row. +) + +// Row represents a portion of a row in an EDS, either left or right half. +type Row struct { + shares []libshare.Share // holds the shares of Left or Right or Both sides of the row. + side RowSide // side indicates which side the shares belong to. +} + +// NewRow creates a new Row with the specified shares and side. +func NewRow(shares []libshare.Share, side RowSide) Row { + return Row{ + shares: shares, + side: side, + } +} + +// RowFromEDS constructs a new Row from an EDS based on the specified row index and side. +func RowFromEDS(eds *rsmt2d.ExtendedDataSquare, rowIdx int, side RowSide) (Row, error) { + rowBytes := eds.Row(uint(rowIdx)) + shares, err := libshare.FromBytes(rowBytes) + if err != nil { + return Row{}, fmt.Errorf("while converting shares from bytes: %w", err) + } + + switch side { + case Both: + case Left: + shares = shares[:len(shares)/2] + case Right: + shares = shares[len(shares)/2:] + default: + return Row{}, fmt.Errorf("invalid RowSide: %d", side) + } + + return NewRow(shares, side), nil +} + +// RowFromProto converts a protobuf Row to a Row structure. +func RowFromProto(r *pb.Row) (Row, error) { + shrs, err := SharesFromProto(r.SharesHalf) + if err != nil { + return Row{}, err + } + return Row{ + shares: shrs, + side: sideFromProto(r.GetHalfSide()), + }, nil +} + +// Shares reconstructs the complete row shares from the half provided, using RSMT2D for data +// recovery if needed. +// It caches the reconstructed shares for future use and converts Row to Both side. +func (r *Row) Shares() ([]libshare.Share, error) { + if r.side == Both { + return r.shares, nil + } + + shares := make([]libshare.Share, len(r.shares)*2) + offset := len(r.shares) * int(r.side) + for i, share := range r.shares { + shares[i+offset] = share + } + + rowShares, err := share.DefaultRSMT2DCodec().Decode(libshare.ToBytes(shares)) + if err != nil { + return nil, err + } + + r.shares, err = libshare.FromBytes(rowShares) + if err != nil { + return nil, err + } + + r.side = Both + return r.shares, nil +} + +// ToProto converts the Row to its protobuf representation. +func (r Row) ToProto() *pb.Row { + if r.side == Both { + // we don't need to send the whole row over the wire + // so if we have both sides, we can save bandwidth and send the left half only + return &pb.Row{ + SharesHalf: SharesToProto(r.shares[:len(r.shares)/2]), + HalfSide: pb.Row_LEFT, + } + } + + return &pb.Row{ + SharesHalf: SharesToProto(r.shares), + HalfSide: r.side.ToProto(), + } +} + +// IsEmpty reports whether the Row is empty, i.e. doesn't contain any shares. +func (r Row) IsEmpty() bool { + return len(r.shares) == 0 +} + +// Verify checks if the row's shares match the expected number from the root data and validates +// the side of the row. +func (r *Row) Verify(roots *share.AxisRoots, idx int) error { + if len(r.shares) == 0 { + return fmt.Errorf("empt row") + } + expectedShares := len(roots.RowRoots) + if r.side != Both { + expectedShares /= 2 + } + if len(r.shares) != expectedShares { + return fmt.Errorf("shares size doesn't match root size: %d != %d", len(r.shares), expectedShares) + } + if r.side != Left && r.side != Right && r.side != Both { + return fmt.Errorf("invalid RowSide: %d", r.side) + } + + if err := r.verifyInclusion(roots, idx); err != nil { + return fmt.Errorf("%w: %w", ErrFailedVerification, err) + } + return nil +} + +// verifyInclusion verifies the integrity of the row's shares against the provided root hash for the +// given row index. +func (r *Row) verifyInclusion(roots *share.AxisRoots, idx int) error { + shrs, err := r.Shares() + if err != nil { + return fmt.Errorf("while extending shares: %w", err) + } + + sqrLn := uint64(len(shrs) / 2) + tree := wrapper.NewErasuredNamespacedMerkleTree(sqrLn, uint(idx)) + for _, s := range shrs { + if err := tree.Push(s.ToBytes()); err != nil { + return fmt.Errorf("while pushing shares to NMT: %w", err) + } + } + + root, err := tree.Root() + if err != nil { + return fmt.Errorf("while computing NMT root: %w", err) + } + + if !bytes.Equal(roots.RowRoots[idx], root) { + return fmt.Errorf("invalid root hash: %X != %X", root, roots.RowRoots[idx]) + } + return nil +} + +// MarshalJSON encodes row to the json encoded bytes. +func (r Row) MarshalJSON() ([]byte, error) { + jsonRow := struct { + Shares []libshare.Share `json:"shares"` + Side string `json:"side"` + }{ + Shares: r.shares, + Side: r.side.String(), + } + return json.Marshal(&jsonRow) +} + +// UnmarshalJSON decodes json bytes to the row. +func (r *Row) UnmarshalJSON(data []byte) error { + jsonRow := struct { + Shares []libshare.Share `json:"shares"` + Side string `json:"side"` + }{} + err := json.Unmarshal(data, &jsonRow) + if err != nil { + return err + } + r.shares = jsonRow.Shares + r.side = toRowSide(jsonRow.Side) + return nil +} + +// ToProto converts a RowSide to its protobuf representation. +func (s RowSide) ToProto() pb.Row_HalfSide { + if s == Left { + return pb.Row_LEFT + } + return pb.Row_RIGHT +} + +// sideFromProto converts a protobuf Row_HalfSide back to a RowSide. +func sideFromProto(side pb.Row_HalfSide) RowSide { + if side == pb.Row_LEFT { + return Left + } + return Right +} + +func (s RowSide) String() string { + switch s { + case Left: + return "LEFT" + case Right: + return "RIGHT" + case Both: + return "BOTH" + default: + panic("invalid row side") + } +} + +func toRowSide(s string) RowSide { + switch s { + case "LEFT": + return Left + case "RIGHT": + return Right + case "BOTH": + return Both + default: + panic("invalid row side") + } +} diff --git a/share/shwap/row_id.go b/share/shwap/row_id.go new file mode 100644 index 0000000000..169458fcbb --- /dev/null +++ b/share/shwap/row_id.go @@ -0,0 +1,125 @@ +package shwap + +import ( + "encoding/binary" + "fmt" + "io" +) + +// RowIDSize defines the size in bytes of RowID, consisting of the size of EdsID and 2 bytes for +// RowIndex. +const RowIDSize = EdsIDSize + 2 + +// RowID uniquely identifies a row in the data square of a blockchain block, combining block height +// with the row's index. +type RowID struct { + EdsID // Embedding EdsID to include the block height in RowID. + RowIndex int // RowIndex specifies the position of the row within the data share. +} + +// NewRowID creates a new RowID with the specified block height, row index, and EDS size. +// It returns an error if the validation fails, ensuring the RowID +// conforms to expected constraints. +func NewRowID(height uint64, rowIdx, edsSize int) (RowID, error) { + rid := RowID{ + EdsID: EdsID{ + Height: height, + }, + RowIndex: rowIdx, + } + if err := rid.Verify(edsSize); err != nil { + return RowID{}, fmt.Errorf("verifying RowID: %w", err) + } + + return rid, nil +} + +// RowIDFromBinary decodes a RowID from its binary representation. +// It returns an error if the input data does not conform to the expected size or content format. +func RowIDFromBinary(data []byte) (RowID, error) { + if len(data) != RowIDSize { + return RowID{}, fmt.Errorf("invalid RowID data length: expected %d, got %d", RowIDSize, len(data)) + } + eid, err := EdsIDFromBinary(data[:EdsIDSize]) + if err != nil { + return RowID{}, fmt.Errorf("decoding EdsID: %w", err) + } + + rid := RowID{ + EdsID: eid, + RowIndex: int(binary.BigEndian.Uint16(data[EdsIDSize:])), + } + if err := rid.Validate(); err != nil { + return RowID{}, fmt.Errorf("validating RowID: %w", err) + } + + return rid, nil +} + +// Equals checks equality of RowID. +func (rid *RowID) Equals(other RowID) bool { + return rid.EdsID.Equals(other.EdsID) && rid.RowIndex == other.RowIndex +} + +// ReadFrom reads the binary form of RowID from the provided reader. +func (rid *RowID) ReadFrom(r io.Reader) (int64, error) { + data := make([]byte, RowIDSize) + n, err := io.ReadFull(r, data) + if err != nil { + return int64(n), err + } + if n != RowIDSize { + return int64(n), fmt.Errorf("RowID: expected %d bytes, got %d", RowIDSize, n) + } + id, err := RowIDFromBinary(data) + if err != nil { + return int64(n), fmt.Errorf("RowIDFromBinary: %w", err) + } + *rid = id + return int64(n), nil +} + +// MarshalBinary encodes the RowID into a binary form for storage or network transmission. +func (rid RowID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, RowIDSize) + return rid.appendTo(data), nil +} + +// WriteTo writes the binary form of RowID to the provided writer. +func (rid RowID) WriteTo(w io.Writer) (int64, error) { + data, err := rid.MarshalBinary() + if err != nil { + return 0, err + } + n, err := w.Write(data) + return int64(n), err +} + +// Verify validates the RowID fields and verifies that RowIndex is within the bounds of +// the square size +func (rid RowID) Verify(edsSize int) error { + if edsSize == 0 { + return fmt.Errorf("provided EDS size is zero") + } + + if rid.RowIndex >= edsSize { + return fmt.Errorf("%w, RowIndex: %d >= %d", ErrOutOfBounds, rid.RowIndex, edsSize) + } + + return rid.Validate() +} + +// Validate performs basic field validation. +func (rid RowID) Validate() error { + if rid.RowIndex < 0 { + return fmt.Errorf("%w: RowIndex: %d < 0", ErrInvalidID, rid.RowIndex) + } + return rid.EdsID.Validate() +} + +// appendTo assists in binary encoding of RowID by appending the encoded fields to the given byte +// slice. +func (rid RowID) appendTo(data []byte) []byte { + data = rid.EdsID.appendTo(data) + return binary.BigEndian.AppendUint16(data, uint16(rid.RowIndex)) +} diff --git a/share/shwap/row_id_test.go b/share/shwap/row_id_test.go new file mode 100644 index 0000000000..fa26315665 --- /dev/null +++ b/share/shwap/row_id_test.go @@ -0,0 +1,46 @@ +package shwap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRowID(t *testing.T) { + edsSize := 4 + + id, err := NewRowID(2, 1, edsSize) + require.NoError(t, err) + + data, err := id.MarshalBinary() + require.NoError(t, err) + + idOut, err := RowIDFromBinary(data) + require.NoError(t, err) + assert.EqualValues(t, id, idOut) + + err = idOut.Verify(edsSize) + require.NoError(t, err) + require.True(t, id.Equals(idOut)) +} + +func TestRowIDReaderWriter(t *testing.T) { + edsSize := 4 + + id, err := NewRowID(2, 1, edsSize) + require.NoError(t, err) + + buf := bytes.NewBuffer(nil) + n, err := id.WriteTo(buf) + require.NoError(t, err) + require.Equal(t, int64(RowIDSize), n) + + ridOut := RowID{} + n, err = ridOut.ReadFrom(buf) + require.NoError(t, err) + require.Equal(t, int64(RowIDSize), n) + + require.EqualValues(t, id, ridOut) +} diff --git a/share/shwap/row_namespace_data.go b/share/shwap/row_namespace_data.go new file mode 100644 index 0000000000..aeebdfdb28 --- /dev/null +++ b/share/shwap/row_namespace_data.go @@ -0,0 +1,226 @@ +package shwap + +import ( + "errors" + "fmt" + "io" + + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + "github.com/celestiaorg/go-libp2p-messenger/serde" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/nmt" + nmt_pb "github.com/celestiaorg/nmt/pb" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap/pb" +) + +// RowNamespaceDataName is the name identifier for the row namespace data container. +const RowNamespaceDataName = "rnd_v0" + +// ErrNamespaceOutsideRange is returned by RowNamespaceDataFromShares when the target namespace is +// outside of the namespace range for the given row. In this case, the implementation cannot return +// the non-inclusion proof and will return ErrNamespaceOutsideRange. +var ErrNamespaceOutsideRange = errors.New("target namespace is outside of namespace range for the given root") + +// RowNamespaceData holds shares and their corresponding proof for a single row within a namespace. +type RowNamespaceData struct { + Shares []libshare.Share `json:"shares"` // Shares within the namespace. + Proof *nmt.Proof `json:"proof"` // Proof of the shares' inclusion in the namespace. +} + +// RowNamespaceDataFromShares extracts and constructs a RowNamespaceData from shares within the +// specified namespace. +func RowNamespaceDataFromShares( + shares []libshare.Share, + namespace libshare.Namespace, + rowIndex int, +) (RowNamespaceData, error) { + tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(len(shares)/2), uint(rowIndex)) + nmtTree := nmt.New( + appconsts.NewBaseHashFunc(), + nmt.NamespaceIDSize(libshare.NamespaceSize), + nmt.IgnoreMaxNamespace(true), + ) + tree.SetTree(nmtTree) + + for _, shr := range shares { + if err := tree.Push(shr.ToBytes()); err != nil { + return RowNamespaceData{}, fmt.Errorf("failed to build tree for row %d: %w", rowIndex, err) + } + } + + root, err := tree.Root() + if err != nil { + return RowNamespaceData{}, fmt.Errorf("failed to get root for row %d: %w", rowIndex, err) + } + outside, err := share.IsOutsideRange(namespace, root, root) + if err != nil { + return RowNamespaceData{}, err + } + if outside { + return RowNamespaceData{}, ErrNamespaceOutsideRange + } + + var from, count int + for i := range len(shares) / 2 { + if namespace.Equals(shares[i].Namespace()) { + if count == 0 { + from = i + } + count++ + continue + } + if count > 0 { + break + } + } + + // if count is 0, then the namespace is not present in the shares. Return non-inclusion proof. + if count == 0 { + proof, err := nmtTree.ProveNamespace(namespace.Bytes()) + if err != nil { + return RowNamespaceData{}, fmt.Errorf("failed to generate non-inclusion proof for row %d: %w", rowIndex, err) + } + + return RowNamespaceData{ + Proof: &proof, + }, nil + } + + namespacedShares := make([]libshare.Share, count) + copy(namespacedShares, shares[from:from+count]) + + proof, err := tree.ProveRange(from, from+count) + if err != nil { + return RowNamespaceData{}, fmt.Errorf("failed to generate proof for row %d: %w", rowIndex, err) + } + + return RowNamespaceData{ + Shares: namespacedShares, + Proof: &proof, + }, nil +} + +// RowNamespaceDataFromProto constructs RowNamespaceData out of its protobuf representation. +func RowNamespaceDataFromProto(row *pb.RowNamespaceData) (RowNamespaceData, error) { + var proof nmt.Proof + if row.GetProof().GetLeafHash() != nil { + proof = nmt.NewAbsenceProof( + int(row.GetProof().GetStart()), + int(row.GetProof().GetEnd()), + row.GetProof().GetNodes(), + row.GetProof().GetLeafHash(), + row.GetProof().GetIsMaxNamespaceIgnored(), + ) + } else { + proof = nmt.NewInclusionProof( + int(row.GetProof().GetStart()), + int(row.GetProof().GetEnd()), + row.GetProof().GetNodes(), + row.GetProof().GetIsMaxNamespaceIgnored(), + ) + } + + shares, err := SharesFromProto(row.GetShares()) + if err != nil { + return RowNamespaceData{}, err + } + + return RowNamespaceData{ + Shares: shares, + Proof: &proof, + }, nil +} + +// ToProto converts RowNamespaceData to its protobuf representation for serialization. +func (rnd RowNamespaceData) ToProto() *pb.RowNamespaceData { + return &pb.RowNamespaceData{ + Shares: SharesToProto(rnd.Shares), + Proof: &nmt_pb.Proof{ + Start: int64(rnd.Proof.Start()), + End: int64(rnd.Proof.End()), + Nodes: rnd.Proof.Nodes(), + LeafHash: rnd.Proof.LeafHash(), + IsMaxNamespaceIgnored: rnd.Proof.IsMaxNamespaceIDIgnored(), + }, + } +} + +// IsEmpty reports whether the RowNamespaceData is empty, i.e. doesn't contain a proof. +func (rnd RowNamespaceData) IsEmpty() bool { + return rnd.Proof == nil +} + +// Verify checks validity of the RowNamespaceData against the AxisRoots, Namespace and Row index. +func (rnd RowNamespaceData) Verify(roots *share.AxisRoots, namespace libshare.Namespace, rowIdx int) error { + if rnd.Proof == nil || rnd.Proof.IsEmptyProof() { + return fmt.Errorf("nil proof") + } + if len(rnd.Shares) == 0 && !rnd.Proof.IsOfAbsence() { + return fmt.Errorf("empty shares with non-absence proof for row %d", rowIdx) + } + + if len(rnd.Shares) > 0 && rnd.Proof.IsOfAbsence() { + return fmt.Errorf("non-empty shares with absence proof for row %d", rowIdx) + } + + rowRoot := roots.RowRoots[rowIdx] + outside, err := share.IsOutsideRange(namespace, rowRoot, rowRoot) + if err != nil { + return err + } + if outside { + return fmt.Errorf("namespace out of range for row %d", rowIdx) + } + + if !rnd.verifyInclusion(rowRoot, namespace) { + return fmt.Errorf("%w for row: %d", ErrFailedVerification, rowIdx) + } + return nil +} + +// verifyInclusion checks the inclusion of the row's shares in the provided root using NMT. +func (rnd RowNamespaceData) verifyInclusion(rowRoot []byte, namespace libshare.Namespace) bool { + leaves := make([][]byte, 0, len(rnd.Shares)) + for _, sh := range rnd.Shares { + namespaceBytes := sh.Namespace().Bytes() + leave := make([]byte, len(sh.ToBytes())+len(namespaceBytes)) + copy(leave, namespaceBytes) + copy(leave[len(namespaceBytes):], sh.ToBytes()) + leaves = append(leaves, leave) + } + + return rnd.Proof.VerifyNamespace( + share.NewSHA256Hasher(), + namespace.Bytes(), + leaves, + rowRoot, + ) +} + +// ReadFrom reads length-delimited protobuf representation of RowNamespaceData +// implementing io.ReaderFrom. +func (rnd *RowNamespaceData) ReadFrom(reader io.Reader) (int64, error) { + var pbrnd pb.RowNamespaceData + n, err := serde.Read(reader, &pbrnd) + if err != nil { + return int64(n), fmt.Errorf("reading RowNamespaceData: %w", err) + } + + *rnd, err = RowNamespaceDataFromProto(&pbrnd) + return int64(n), err +} + +// WriteTo writes length-delimited protobuf representation of RowNamespaceData. +// implementing io.WriterTo. +func (rnd RowNamespaceData) WriteTo(writer io.Writer) (int64, error) { + pbrnd := rnd.ToProto() + n, err := serde.Write(writer, pbrnd) + if err != nil { + return int64(n), fmt.Errorf("writing RowNamespaceData: %w", err) + } + + return int64(n), nil +} diff --git a/share/shwap/row_namespace_data_id.go b/share/shwap/row_namespace_data_id.go new file mode 100644 index 0000000000..2d0444e1ea --- /dev/null +++ b/share/shwap/row_namespace_data_id.go @@ -0,0 +1,137 @@ +package shwap + +import ( + "fmt" + "io" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +// RowNamespaceDataIDSize defines the total size of a RowNamespaceDataID in bytes, combining the +// size of a RowID and the size of a Namespace. +const RowNamespaceDataIDSize = RowIDSize + libshare.NamespaceSize + +// RowNamespaceDataID uniquely identifies a piece of namespaced data within a row of an Extended +// Data Square (EDS). +type RowNamespaceDataID struct { + RowID // Embedded RowID representing the specific row in the EDS. + // DataNamespace is used to facilitate comparisons. + DataNamespace libshare.Namespace +} + +// NewRowNamespaceDataID creates a new RowNamespaceDataID with the specified parameters. It +// validates the RowNamespaceDataID against the provided EDS size. +func NewRowNamespaceDataID( + height uint64, + rowIdx int, + namespace libshare.Namespace, + edsSize int, +) (RowNamespaceDataID, error) { + did := RowNamespaceDataID{ + RowID: RowID{ + EdsID: EdsID{ + Height: height, + }, + RowIndex: rowIdx, + }, + DataNamespace: namespace, + } + + if err := did.Verify(edsSize); err != nil { + return RowNamespaceDataID{}, fmt.Errorf("verifying RowNamespaceDataID: %w", err) + } + return did, nil +} + +// RowNamespaceDataIDFromBinary deserializes a RowNamespaceDataID from its binary form. It returns +// an error if the binary data's length does not match the expected size. +func RowNamespaceDataIDFromBinary(data []byte) (RowNamespaceDataID, error) { + if len(data) != RowNamespaceDataIDSize { + return RowNamespaceDataID{}, + fmt.Errorf("invalid RowNamespaceDataID length: expected %d, got %d", RowNamespaceDataIDSize, len(data)) + } + + rid, err := RowIDFromBinary(data[:RowIDSize]) + if err != nil { + return RowNamespaceDataID{}, fmt.Errorf("unmarshaling RowID: %w", err) + } + + ns, err := libshare.NewNamespaceFromBytes(data[RowIDSize:]) + if err != nil { + return RowNamespaceDataID{}, fmt.Errorf("invalid namespace format: %w", err) + } + + return RowNamespaceDataID{ + RowID: rid, + DataNamespace: ns, + }, nil +} + +// Equals checks equality of RowNamespaceDataID. +func (rndid *RowNamespaceDataID) Equals(other RowNamespaceDataID) bool { + return rndid.RowID.Equals(other.RowID) && rndid.DataNamespace.Equals(other.DataNamespace) +} + +// ReadFrom reads the binary form of RowNamespaceDataID from the provided reader. +func (rndid *RowNamespaceDataID) ReadFrom(r io.Reader) (int64, error) { + data := make([]byte, RowNamespaceDataIDSize) + n, err := io.ReadFull(r, data) + if err != nil { + return int64(n), err + } + if n != RowNamespaceDataIDSize { + return int64(n), fmt.Errorf("RowNamespaceDataID: expected %d bytes, got %d", RowNamespaceDataIDSize, n) + } + id, err := RowNamespaceDataIDFromBinary(data) + if err != nil { + return int64(n), fmt.Errorf("RowNamespaceDataIDFromBinary: %w", err) + } + *rndid = id + return int64(n), nil +} + +// MarshalBinary encodes RowNamespaceDataID into binary form. +// NOTE: Proto is avoided because +// * Its size is not deterministic which is required for IPLD. +// * No support for uint16 +func (rndid RowNamespaceDataID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, RowNamespaceDataIDSize) + return rndid.appendTo(data), nil +} + +// WriteTo writes the binary form of RowNamespaceDataID to the provided writer. +func (rndid RowNamespaceDataID) WriteTo(w io.Writer) (int64, error) { + data, err := rndid.MarshalBinary() + if err != nil { + return 0, err + } + n, err := w.Write(data) + return int64(n), err +} + +// Verify validates the RowNamespaceDataID and verifies the embedded RowID. +func (rndid RowNamespaceDataID) Verify(edsSize int) error { + if err := rndid.RowID.Verify(edsSize); err != nil { + return fmt.Errorf("error verifying RowID: %w", err) + } + + return rndid.Validate() +} + +// Validate performs basic field validation. +func (rndid RowNamespaceDataID) Validate() error { + if err := rndid.RowID.Validate(); err != nil { + return fmt.Errorf("validating RowID: %w", err) + } + if err := rndid.DataNamespace.ValidateForData(); err != nil { + return fmt.Errorf("%w: validating DataNamespace: %w", ErrInvalidID, err) + } + + return nil +} + +// appendTo helps in appending the binary form of DataNamespace to the serialized RowID data. +func (rndid RowNamespaceDataID) appendTo(data []byte) []byte { + data = rndid.RowID.appendTo(data) + return append(data, rndid.DataNamespace.Bytes()...) +} diff --git a/share/shwap/row_namespace_data_id_test.go b/share/shwap/row_namespace_data_id_test.go new file mode 100644 index 0000000000..114e220f9b --- /dev/null +++ b/share/shwap/row_namespace_data_id_test.go @@ -0,0 +1,50 @@ +package shwap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +func TestRowNamespaceDataID(t *testing.T) { + edsSize := 4 + ns := libshare.RandomNamespace() + + id, err := NewRowNamespaceDataID(1, 1, ns, edsSize) + require.NoError(t, err) + + data, err := id.MarshalBinary() + require.NoError(t, err) + + sidOut, err := RowNamespaceDataIDFromBinary(data) + require.NoError(t, err) + assert.EqualValues(t, id, sidOut) + + err = sidOut.Verify(edsSize) + require.NoError(t, err) + require.True(t, id.Equals(sidOut)) +} + +func TestRowNamespaceDataIDReaderWriter(t *testing.T) { + edsSize := 4 + ns := libshare.RandomNamespace() + + id, err := NewRowNamespaceDataID(1, 1, ns, edsSize) + require.NoError(t, err) + + buf := bytes.NewBuffer(nil) + n, err := id.WriteTo(buf) + require.NoError(t, err) + require.Equal(t, int64(RowNamespaceDataIDSize), n) + + rndidOut := RowNamespaceDataID{} + n, err = rndidOut.ReadFrom(buf) + require.NoError(t, err) + require.Equal(t, int64(RowNamespaceDataIDSize), n) + + require.EqualValues(t, id, rndidOut) +} diff --git a/share/shwap/row_namespace_data_test.go b/share/shwap/row_namespace_data_test.go new file mode 100644 index 0000000000..cdc383f2e6 --- /dev/null +++ b/share/shwap/row_namespace_data_test.go @@ -0,0 +1,109 @@ +package shwap_test + +import ( + "bytes" + "context" + "slices" + "testing" + "time" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestNamespacedRowFromShares(t *testing.T) { + const odsSize = 8 + + minNamespace, err := libshare.NewV0Namespace(slices.Concat(bytes.Repeat([]byte{0}, 8), []byte{1, 0})) + require.NoError(t, err) + require.NoError(t, minNamespace.ValidateForData()) + + for namespacedAmount := 1; namespacedAmount < odsSize; namespacedAmount++ { + shares, err := libshare.RandSharesWithNamespace(minNamespace, namespacedAmount, odsSize) + require.NoError(t, err) + parity, err := share.DefaultRSMT2DCodec().Encode(libshare.ToBytes(shares)) + require.NoError(t, err) + + paritySh, err := libshare.FromBytes(parity) + require.NoError(t, err) + extended := slices.Concat(shares, paritySh) + + nr, err := shwap.RowNamespaceDataFromShares(extended, minNamespace, 0) + require.NoError(t, err) + require.Equal(t, namespacedAmount, len(nr.Shares)) + } +} + +// func TestNamespacedRowFromSharesNonIncluded(t *testing.T) { +// //TODO: this will fail until absence proof support is added +// t.Skip() +// +// const odsSize = 8 +// //Test absent namespace +// shares,err := libshare.RandShares( odsSize) +// require.NoError(t, err) +// absentNs, err := share.GetNamespace(shares[0]).AddInt(1) +// require.NoError(t, err) +// +// parity, err := share.DefaultRSMT2DCodec().Encode(shares) +// require.NoError(t, err) +// extended := slices.Concat(shares, parity) +// +// shrs, err := libshare.FromBytes(extended) +// require.NoError(t, err) +// +// nr, err := shwap.RowNamespaceDataFromShares(shrs, absentNs, 0) +// require.NoError(t, err) +// require.Len(t, nr.Shares, 0) +// require.True(t, nr.Proof.IsOfAbsence()) +//} + +func TestValidateNamespacedRow(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + + const odsSize = 8 + sharesAmount := odsSize * odsSize + namespace := libshare.RandomNamespace() + for amount := 1; amount < sharesAmount; amount++ { + randEDS, root := edstest.RandEDSWithNamespace(t, namespace, amount, odsSize) + rsmt2d := &eds.Rsmt2D{ExtendedDataSquare: randEDS} + nd, err := eds.NamespaceData(ctx, rsmt2d, namespace) + require.NoError(t, err) + require.True(t, len(nd) > 0) + + rowIdxs, err := share.RowsWithNamespace(root, namespace) + require.NoError(t, err) + require.Len(t, nd, len(rowIdxs)) + + for i, rowIdx := range rowIdxs { + err = nd[i].Verify(root, namespace, rowIdx) + require.NoError(t, err) + } + } +} + +func TestNamespacedRowProtoEncoding(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + + const odsSize = 8 + namespace := libshare.RandomNamespace() + randEDS, _ := edstest.RandEDSWithNamespace(t, namespace, odsSize, odsSize) + rsmt2d := &eds.Rsmt2D{ExtendedDataSquare: randEDS} + nd, err := eds.NamespaceData(ctx, rsmt2d, namespace) + require.NoError(t, err) + require.True(t, len(nd) > 0) + + expected := nd[0] + pb := expected.ToProto() + ndOut, err := shwap.RowNamespaceDataFromProto(pb) + require.NoError(t, err) + require.Equal(t, expected, ndOut) +} diff --git a/share/shwap/row_test.go b/share/shwap/row_test.go new file mode 100644 index 0000000000..3cf80befca --- /dev/null +++ b/share/shwap/row_test.go @@ -0,0 +1,150 @@ +package shwap + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" +) + +func TestRowShares(t *testing.T) { + const odsSize = 8 + eds := edstest.RandEDS(t, odsSize) + + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for _, side := range []RowSide{Left, Right, Both} { + row, err := RowFromEDS(eds, rowIdx, side) + require.NoError(t, err) + require.Equal(t, side, row.side) + + extended, err := row.Shares() + require.NoError(t, err) + require.Len(t, extended, odsSize*2) + require.Equal(t, Both, row.side) + } + } +} + +func TestRowMarshal(t *testing.T) { + const odsSize = 8 + eds := edstest.RandEDS(t, odsSize) + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for _, side := range []RowSide{Left, Right, Both} { + row, err := RowFromEDS(eds, rowIdx, side) + require.NoError(t, err) + rowData, err := json.Marshal(row) + require.NoError(t, err) + + decodedRow := &Row{} + err = json.Unmarshal(rowData, decodedRow) + require.NoError(t, err) + + require.Equal(t, side, decodedRow.side) + extended, err := decodedRow.Shares() + require.NoError(t, err) + + shares, err := row.Shares() + require.NoError(t, err) + + require.Equal(t, shares, extended) + require.Equal(t, row.side, decodedRow.side) + } + } +} + +func TestRowValidate(t *testing.T) { + const odsSize = 8 + eds := edstest.RandEDS(t, odsSize) + root, err := share.NewAxisRoots(eds) + require.NoError(t, err) + + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for _, side := range []RowSide{Left, Right, Both} { + row, err := RowFromEDS(eds, rowIdx, side) + require.NoError(t, err) + + err = row.Verify(root, rowIdx) + require.NoError(t, err) + err = row.Verify(root, rowIdx) + require.NoError(t, err) + } + } +} + +func TestRowValidateNegativeCases(t *testing.T) { + eds := edstest.RandEDS(t, 8) // Generate a random Extended Data Square of size 8 + root, err := share.NewAxisRoots(eds) + require.NoError(t, err) + shrs := eds.Row(0) + shares, err := libshare.FromBytes(shrs) + require.NoError(t, err) + row := NewRow(shares, Left) + + // Test with incorrect side specification + invalidSideRow := Row{shares: row.shares, side: RowSide(999)} + err = invalidSideRow.Verify(root, 0) + require.Error(t, err, "should error on invalid row side") + + // Test with invalid shares (more shares than expected) + incorrectShares := make([]libshare.Share, (eds.Width()/2)+1) // Adding an extra share + for i := range incorrectShares { + shr, err := libshare.NewShare(eds.GetCell(uint(i), 0)) + require.NoError(t, err) + incorrectShares[i] = *shr + } + invalidRow := Row{shares: incorrectShares, side: Left} + err = invalidRow.Verify(root, 0) + require.Error(t, err, "should error on incorrect number of shares") + + // Test with empty shares + emptyRow := Row{shares: []libshare.Share{}, side: Left} + err = emptyRow.Verify(root, 0) + require.Error(t, err, "should error on empty halfShares") + + // Doesn't match root. Corrupt root hash + root.RowRoots[0][len(root.RowRoots[0])-1] ^= 0xFF + err = row.Verify(root, 0) + require.Error(t, err, "should error on invalid root hash") +} + +func TestRowProtoEncoding(t *testing.T) { + const odsSize = 8 + eds := edstest.RandEDS(t, odsSize) + + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for _, side := range []RowSide{Left, Right, Both} { + row, err := RowFromEDS(eds, rowIdx, side) + require.NoError(t, err) + + pb := row.ToProto() + rowOut, err := RowFromProto(pb) + require.NoError(t, err) + if side == Both { + require.NotEqual(t, row, rowOut) + } else { + require.Equal(t, row, rowOut) + } + } + } +} + +// BenchmarkRowValidate benchmarks the performance of row validation. +// BenchmarkRowValidate-10 9591 121802 ns/op +func BenchmarkRowValidate(b *testing.B) { + const odsSize = 32 + eds := edstest.RandEDS(b, odsSize) + root, err := share.NewAxisRoots(eds) + require.NoError(b, err) + row, err := RowFromEDS(eds, 0, Left) + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = row.Verify(root, 0) + } +} diff --git a/share/shwap/sample.go b/share/shwap/sample.go new file mode 100644 index 0000000000..9b8e16a93d --- /dev/null +++ b/share/shwap/sample.go @@ -0,0 +1,134 @@ +package shwap + +import ( + "errors" + "fmt" + + "github.com/celestiaorg/celestia-app/v3/pkg/wrapper" + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/nmt" + nmt_pb "github.com/celestiaorg/nmt/pb" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/shwap/pb" +) + +// SampleName is the name identifier for the sample container. +const SampleName = "sample_v0" + +// ErrFailedVerification is returned when inclusion proof verification fails. It is returned +// when the data and the proof do not match trusted data root. +var ErrFailedVerification = errors.New("failed to verify inclusion") + +// Sample represents a data share along with its Merkle proof, used to validate the share's +// inclusion in a data square. +type Sample struct { + libshare.Share // Embeds the Share which includes the data with namespace. + Proof *nmt.Proof // Proof is the Merkle Proof validating the share's inclusion. + ProofType rsmt2d.Axis // ProofType indicates whether the proof is against a row or a column. +} + +// SampleFromShares creates a Sample from a list of shares, using the specified proof type and +// the share index to be included in the sample. +func SampleFromShares(shares []libshare.Share, proofType rsmt2d.Axis, idx SampleCoords) (Sample, error) { + tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(len(shares)/2), uint(idx.Row)) + for _, shr := range shares { + err := tree.Push(shr.ToBytes()) + if err != nil { + return Sample{}, err + } + } + + proof, err := tree.ProveRange(idx.Col, idx.Col+1) + if err != nil { + return Sample{}, err + } + + return Sample{ + Share: shares[idx.Col], + Proof: &proof, + ProofType: proofType, + }, nil +} + +// SampleFromProto converts a protobuf Sample back into its domain model equivalent. +func SampleFromProto(s *pb.Sample) (Sample, error) { + proof := nmt.NewInclusionProof( + int(s.GetProof().GetStart()), + int(s.GetProof().GetEnd()), + s.GetProof().GetNodes(), + s.GetProof().GetIsMaxNamespaceIgnored(), + ) + + shrs, err := ShareFromProto(s.GetShare()) + if err != nil { + return Sample{}, err + } + + return Sample{ + Share: shrs, + Proof: &proof, + ProofType: rsmt2d.Axis(s.GetProofType()), + }, nil +} + +// ToProto converts a Sample into its protobuf representation for serialization purposes. +func (s Sample) ToProto() *pb.Sample { + return &pb.Sample{ + Share: &pb.Share{Data: s.Share.ToBytes()}, + Proof: &nmt_pb.Proof{ + Start: int64(s.Proof.Start()), + End: int64(s.Proof.End()), + Nodes: s.Proof.Nodes(), + LeafHash: s.Proof.LeafHash(), + IsMaxNamespaceIgnored: s.Proof.IsMaxNamespaceIDIgnored(), + }, + ProofType: pb.AxisType(s.ProofType), + } +} + +// IsEmpty reports whether the Sample is empty, i.e. doesn't contain a proof. +func (s Sample) IsEmpty() bool { + return s.Proof == nil +} + +// Verify checks the inclusion of the share using its Merkle proof under the specified AxisRoots. +// Returns an error if the proof is invalid or does not correspond to the indicated proof type. +func (s Sample) Verify(roots *share.AxisRoots, rowIdx, colIdx int) error { + if s.Proof == nil || s.Proof.IsEmptyProof() { + return errors.New("nil proof") + } + if s.ProofType != rsmt2d.Row && s.ProofType != rsmt2d.Col { + return fmt.Errorf("invalid SampleProofType: %d", s.ProofType) + } + if !s.verifyInclusion(roots, rowIdx, colIdx) { + return ErrFailedVerification + } + return nil +} + +// verifyInclusion checks if the share is included in the given root hash at the specified indices. +func (s Sample) verifyInclusion(roots *share.AxisRoots, rowIdx, colIdx int) bool { + size := len(roots.RowRoots) + namespace := inclusionNamespace(s.Share, rowIdx, colIdx, size) + rootHash := share.RootHashForCoordinates(roots, s.ProofType, uint(rowIdx), uint(colIdx)) + return s.Proof.VerifyInclusion( + share.NewSHA256Hasher(), + namespace.Bytes(), + [][]byte{s.Share.ToBytes()}, + rootHash, + ) +} + +// inclusionNamespace returns the namespace for the share based on its position in the share. +// Shares from extended part of the square are considered parity shares. It means that +// parity shares are located outside of first quadrant of the square. According to the nmt +// specification, the parity shares are prefixed with the namespace of the parity shares. +func inclusionNamespace(sh libshare.Share, rowIdx, colIdx, squareSize int) libshare.Namespace { + isParity := colIdx >= squareSize/2 || rowIdx >= squareSize/2 + if isParity { + return libshare.ParitySharesNamespace + } + return sh.Namespace() +} diff --git a/share/shwap/sample_id.go b/share/shwap/sample_id.go new file mode 100644 index 0000000000..a3b13279fd --- /dev/null +++ b/share/shwap/sample_id.go @@ -0,0 +1,153 @@ +package shwap + +import ( + "encoding/binary" + "fmt" + "io" +) + +// SampleIDSize defines the size of the SampleID in bytes, combining RowID size and 2 additional +// bytes for the ShareIndex. +const SampleIDSize = RowIDSize + 2 + +type SampleCoords struct { + Row int `json:"row"` + Col int `json:"col"` +} + +func SampleCoordsAs1DIndex(idx SampleCoords, edsSize int) (int, error) { + if idx.Row < 0 || idx.Col < 0 { + return 0, fmt.Errorf("negative row or col index: %w", ErrInvalidID) + } + if idx.Row >= edsSize || idx.Col >= edsSize { + return 0, fmt.Errorf("SampleCoords %d || %d > %d: %w", idx.Row, idx.Col, edsSize, ErrOutOfBounds) + } + return idx.Row*edsSize + idx.Col, nil +} + +func SampleCoordsFrom1DIndex(idx, squareSize int) (SampleCoords, error) { + if idx > squareSize*squareSize { + return SampleCoords{}, fmt.Errorf("SampleCoords %d > %d: %w", idx, squareSize*squareSize, ErrOutOfBounds) + } + + rowIdx := idx / squareSize + colIdx := idx % squareSize + return SampleCoords{Row: rowIdx, Col: colIdx}, nil +} + +// SampleID uniquely identifies a specific sample within a row of an Extended Data Square (EDS). +type SampleID struct { + RowID // Embeds RowID to incorporate block height and row index. + ShareIndex int // ShareIndex specifies the index of the sample within the row. +} + +// NewSampleID constructs a new SampleID using the provided block height, sample index, and EDS +// size. It calculates the row and share index based on the sample index and EDS size. +func NewSampleID(height uint64, idx SampleCoords, edsSize int) (SampleID, error) { + sid := SampleID{ + RowID: RowID{ + EdsID: EdsID{ + Height: height, + }, + RowIndex: idx.Row, + }, + ShareIndex: idx.Col, + } + + if err := sid.Verify(edsSize); err != nil { + return SampleID{}, fmt.Errorf("verifying SampleID: %w", err) + } + return sid, nil +} + +// SampleIDFromBinary deserializes a SampleID from binary data, ensuring the data length matches +// the expected size. +func SampleIDFromBinary(data []byte) (SampleID, error) { + if len(data) != SampleIDSize { + return SampleID{}, fmt.Errorf("invalid SampleID data length: expected %d, got %d", SampleIDSize, len(data)) + } + + rid, err := RowIDFromBinary(data[:RowIDSize]) + if err != nil { + return SampleID{}, fmt.Errorf("decoding RowID: %w", err) + } + + sid := SampleID{ + RowID: rid, + ShareIndex: int(binary.BigEndian.Uint16(data[RowIDSize:])), + } + if err := sid.Validate(); err != nil { + return SampleID{}, fmt.Errorf("validating SampleID: %w", err) + } + + return sid, nil +} + +// Equals checks equality of SampleID. +func (sid *SampleID) Equals(other SampleID) bool { + return sid.RowID.Equals(other.RowID) && sid.ShareIndex == other.ShareIndex +} + +// ReadFrom reads the binary form of SampleID from the provided reader. +func (sid *SampleID) ReadFrom(r io.Reader) (int64, error) { + data := make([]byte, SampleIDSize) + n, err := io.ReadFull(r, data) + if err != nil { + return int64(n), err + } + if n != SampleIDSize { + return int64(n), fmt.Errorf("SampleID: expected %d bytes, got %d", SampleIDSize, n) + } + id, err := SampleIDFromBinary(data) + if err != nil { + return int64(n), fmt.Errorf("SampleIDFromBinary: %w", err) + } + *sid = id + return int64(n), nil +} + +// MarshalBinary encodes SampleID into binary form. +// NOTE: Proto is avoided because +// * Its size is not deterministic which is required for IPLD. +// * No support for uint16 +func (sid SampleID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, SampleIDSize) + return sid.appendTo(data), nil +} + +// WriteTo writes the binary form of SampleID to the provided writer. +func (sid SampleID) WriteTo(w io.Writer) (int64, error) { + data, err := sid.MarshalBinary() + if err != nil { + return 0, err + } + n, err := w.Write(data) + return int64(n), err +} + +// Verify validates the SampleID and verifies that the ShareIndex is within the bounds of +// the square size. +func (sid SampleID) Verify(edsSize int) error { + if err := sid.RowID.Verify(edsSize); err != nil { + return fmt.Errorf("verifying RowID: %w", err) + } + if sid.ShareIndex >= edsSize { + return fmt.Errorf("%w: ShareIndex: %d >= %d", ErrOutOfBounds, sid.ShareIndex, edsSize) + } + return sid.Validate() +} + +// Validate performs basic field validation. +func (sid SampleID) Validate() error { + if sid.ShareIndex < 0 { + return fmt.Errorf("%w: ShareIndex: %d < 0", ErrInvalidID, sid.ShareIndex) + } + return sid.RowID.Validate() +} + +// appendTo helps in constructing the binary representation by appending the encoded ShareIndex to +// the serialized RowID. +func (sid SampleID) appendTo(data []byte) []byte { + data = sid.RowID.appendTo(data) + return binary.BigEndian.AppendUint16(data, uint16(sid.ShareIndex)) +} diff --git a/share/shwap/sample_id_test.go b/share/shwap/sample_id_test.go new file mode 100644 index 0000000000..3df2f6ce56 --- /dev/null +++ b/share/shwap/sample_id_test.go @@ -0,0 +1,58 @@ +package shwap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSampleID(t *testing.T) { + edsSize := 4 + + id, err := NewSampleID(1, SampleCoords{Col: 1}, edsSize) + require.NoError(t, err) + + data, err := id.MarshalBinary() + require.NoError(t, err) + + idOut, err := SampleIDFromBinary(data) + require.NoError(t, err) + assert.EqualValues(t, id, idOut) + + err = idOut.Verify(edsSize) + require.NoError(t, err) + require.True(t, id.Equals(idOut)) +} + +func TestSampleIDReaderWriter(t *testing.T) { + edsSize := 4 + + id, err := NewSampleID(1, SampleCoords{Col: 1}, edsSize) + require.NoError(t, err) + + buf := bytes.NewBuffer(nil) + n, err := id.WriteTo(buf) + require.NoError(t, err) + require.Equal(t, int64(SampleIDSize), n) + + sidOut := SampleID{} + n, err = sidOut.ReadFrom(buf) + require.NoError(t, err) + require.Equal(t, int64(SampleIDSize), n) + + require.EqualValues(t, id, sidOut) +} + +func TestSampleCoords(t *testing.T) { + edsSize := 16 + + rawIdx := 13 * 16 + idxIn, err := SampleCoordsFrom1DIndex(rawIdx, edsSize) + require.NoError(t, err) + + idxOut, err := SampleCoordsAs1DIndex(idxIn, edsSize) + require.NoError(t, err) + assert.Equal(t, rawIdx, idxOut) +} diff --git a/share/shwap/sample_test.go b/share/shwap/sample_test.go new file mode 100644 index 0000000000..0c03eb47cc --- /dev/null +++ b/share/shwap/sample_test.go @@ -0,0 +1,118 @@ +package shwap_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestSampleValidate(t *testing.T) { + const odsSize = 8 + randEDS := edstest.RandEDS(t, odsSize) + root, err := share.NewAxisRoots(randEDS) + require.NoError(t, err) + inMem := eds.Rsmt2D{ExtendedDataSquare: randEDS} + + for _, proofType := range []rsmt2d.Axis{rsmt2d.Row, rsmt2d.Col} { + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for colIdx := 0; colIdx < odsSize*2; colIdx++ { + idx := shwap.SampleCoords{Row: rowIdx, Col: colIdx} + + sample, err := inMem.SampleForProofAxis(idx, proofType) + require.NoError(t, err) + + require.NoError(t, sample.Verify(root, rowIdx, colIdx), "row: %d col: %d", rowIdx, colIdx) + } + } + } +} + +// TestSampleNegativeVerifyInclusion checks +func TestSampleNegativeVerifyInclusion(t *testing.T) { + const odsSize = 8 + randEDS := edstest.RandEDS(t, odsSize) + root, err := share.NewAxisRoots(randEDS) + require.NoError(t, err) + inMem := eds.Rsmt2D{ExtendedDataSquare: randEDS} + + sample, err := inMem.Sample(context.Background(), shwap.SampleCoords{}) + require.NoError(t, err) + err = sample.Verify(root, 0, 0) + require.NoError(t, err) + + // incorrect row index + err = sample.Verify(root, 1, 0) + require.ErrorIs(t, err, shwap.ErrFailedVerification) + + // Corrupt the share + b := sample.Share.ToBytes() + b[0] ^= 0xFF + shr, err := libshare.NewShare(b) + require.NoError(t, err) + sample.Share = *shr + err = sample.Verify(root, 0, 0) + require.ErrorIs(t, err, shwap.ErrFailedVerification) + + // incorrect proofType + sample, err = inMem.Sample(context.Background(), shwap.SampleCoords{}) + require.NoError(t, err) + sample.ProofType = rsmt2d.Col + err = sample.Verify(root, 0, 0) + require.ErrorIs(t, err, shwap.ErrFailedVerification) + + // Corrupt the last root hash byte + sample, err = inMem.Sample(context.Background(), shwap.SampleCoords{}) + require.NoError(t, err) + root.RowRoots[0][len(root.RowRoots[0])-1] ^= 0xFF + err = sample.Verify(root, 0, 0) + require.ErrorIs(t, err, shwap.ErrFailedVerification) +} + +func TestSampleProtoEncoding(t *testing.T) { + const odsSize = 8 + randEDS := edstest.RandEDS(t, odsSize) + inMem := eds.Rsmt2D{ExtendedDataSquare: randEDS} + + for _, proofType := range []rsmt2d.Axis{rsmt2d.Row, rsmt2d.Col} { + for rowIdx := 0; rowIdx < odsSize*2; rowIdx++ { + for colIdx := 0; colIdx < odsSize*2; colIdx++ { + idx := shwap.SampleCoords{Row: rowIdx, Col: colIdx} + + sample, err := inMem.SampleForProofAxis(idx, proofType) + require.NoError(t, err) + + pb := sample.ToProto() + sampleOut, err := shwap.SampleFromProto(pb) + require.NoError(t, err) + require.Equal(t, sample, sampleOut) + } + } + } +} + +// BenchmarkSampleValidate benchmarks the performance of sample validation. +// BenchmarkSampleValidate-10 284829 3935 ns/op +func BenchmarkSampleValidate(b *testing.B) { + const odsSize = 32 + randEDS := edstest.RandEDS(b, odsSize) + root, err := share.NewAxisRoots(randEDS) + require.NoError(b, err) + inMem := eds.Rsmt2D{ExtendedDataSquare: randEDS} + + sample, err := inMem.SampleForProofAxis(shwap.SampleCoords{}, rsmt2d.Row) + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = sample.Verify(root, 0, 0) + } +} diff --git a/share/shwap/share.go b/share/shwap/share.go new file mode 100644 index 0000000000..3f8205498c --- /dev/null +++ b/share/shwap/share.go @@ -0,0 +1,48 @@ +package shwap + +import ( + "fmt" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/share/shwap/pb" +) + +// ShareFromProto converts a protobuf Share object to the application's internal share +// representation. It returns nil if the input protobuf Share is nil, ensuring safe handling of nil +// values. +func ShareFromProto(s *pb.Share) (libshare.Share, error) { + if s == nil { + return libshare.Share{}, nil + } + sh, err := libshare.NewShare(s.Data) + if err != nil { + return libshare.Share{}, err + } + return *sh, err +} + +// SharesToProto converts a slice of Shares from the application's internal representation to a +// slice of protobuf Share objects. This function allocates memory for the protobuf objects and +// copies data from the input slice. +func SharesToProto(shrs []libshare.Share) []*pb.Share { + protoShares := make([]*pb.Share, len(shrs)) + for i, shr := range shrs { + protoShares[i] = &pb.Share{Data: shr.ToBytes()} + } + return protoShares +} + +// SharesFromProto converts a slice of protobuf Share objects to the application's internal slice +// of Shares. It ensures that each Share is correctly transformed using the ShareFromProto function. +func SharesFromProto(shrs []*pb.Share) ([]libshare.Share, error) { + shares := make([]libshare.Share, len(shrs)) + var err error + for i, shr := range shrs { + shares[i], err = ShareFromProto(shr) + if err != nil { + return nil, fmt.Errorf("invalid share at index %d: %w", i, err) + } + } + return shares, nil +} diff --git a/state/core_access.go b/state/core_access.go index caaaa2feff..a363577b1c 100644 --- a/state/core_access.go +++ b/state/core_access.go @@ -22,11 +22,12 @@ import ( "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" - apperrors "github.com/celestiaorg/celestia-app/v2/app/errors" - "github.com/celestiaorg/celestia-app/v2/pkg/user" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" + apperrors "github.com/celestiaorg/celestia-app/v3/app/errors" + "github.com/celestiaorg/celestia-app/v3/pkg/user" libhead "github.com/celestiaorg/go-header" + libshare "github.com/celestiaorg/go-square/v2/share" "github.com/celestiaorg/celestia-node/header" ) @@ -69,6 +70,7 @@ type CoreAccessor struct { coreConn *grpc.ClientConn coreIP string grpcPort string + network string // these fields are mutatable and thus need to be protected by a mutex lock sync.Mutex @@ -90,6 +92,7 @@ func NewCoreAccessor( getter libhead.Head[*header.ExtendedHeader], coreIP, grpcPort string, + network string, options ...Option, ) (*CoreAccessor, error) { // create verifier @@ -104,6 +107,7 @@ func NewCoreAccessor( coreIP: coreIP, grpcPort: grpcPort, prt: prt, + network: network, } for _, opt := range options { @@ -142,6 +146,15 @@ func (ca *CoreAccessor) Start(ctx context.Context) error { // create ABCI query client ca.abciQueryCli = tmservice.NewServiceClient(ca.coreConn) + resp, err := ca.abciQueryCli.GetNodeInfo(ctx, &tmservice.GetNodeInfoRequest{}) + if err != nil { + return fmt.Errorf("failed to get node info: %w", err) + } + + defaultNetwork := resp.GetDefaultNodeInfo().GetNetwork() + if defaultNetwork != ca.network { + return fmt.Errorf("wrong network in core.ip endpoint, expected %s, got %s", ca.network, defaultNetwork) + } // set up signer to handle tx submission ca.client, err = ca.setupTxClient(ctx, ca.defaultSignerAccount) @@ -188,10 +201,10 @@ func (ca *CoreAccessor) cancelCtx() { // TxResponse. The user can specify additional options that can bee applied to the Tx. func (ca *CoreAccessor) SubmitPayForBlob( ctx context.Context, - appblobs []*Blob, + libBlobs []*libshare.Blob, cfg *TxConfig, ) (*TxResponse, error) { - if len(appblobs) == 0 { + if len(libBlobs) == 0 { return nil, errors.New("state: no blobs provided") } @@ -206,9 +219,9 @@ func (ca *CoreAccessor) SubmitPayForBlob( gas := cfg.GasLimit() if gas == 0 { - blobSizes := make([]uint32, len(appblobs)) - for i, blob := range appblobs { - blobSizes[i] = uint32(len(blob.GetData())) + blobSizes := make([]uint32, len(libBlobs)) + for i, blob := range libBlobs { + blobSizes[i] = uint32(len(blob.Data())) } gas = estimateGasForBlobs(blobSizes) } @@ -239,7 +252,7 @@ func (ca *CoreAccessor) SubmitPayForBlob( opts = append(opts, feeGrant) } - response, err := ca.client.SubmitPayForBlobWithAccount(ctx, accName, appblobs, opts...) + response, err := ca.client.SubmitPayForBlobWithAccount(ctx, accName, libBlobs, opts...) // Network min gas price can be updated through governance in app // If that's the case, we parse the insufficient min gas price error message and update the gas price if apperrors.IsInsufficientMinGasPrice(err) { diff --git a/state/core_access_test.go b/state/core_access_test.go index e8120adc39..c487944749 100644 --- a/state/core_access_test.go +++ b/state/core_access_test.go @@ -11,16 +11,15 @@ import ( "time" sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/celestia-app/v2/app" - appconsts "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - genesis "github.com/celestiaorg/celestia-app/v2/test/util/genesis" - "github.com/celestiaorg/celestia-app/v2/test/util/testnode" - apptypes "github.com/celestiaorg/celestia-app/v2/x/blob/types" - squareblob "github.com/celestiaorg/go-square/blob" - - "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/test/util/genesis" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" + apptypes "github.com/celestiaorg/celestia-app/v3/x/blob/types" + libshare "github.com/celestiaorg/go-square/v2/share" ) func TestSubmitPayForBlob(t *testing.T) { @@ -33,30 +32,33 @@ func TestSubmitPayForBlob(t *testing.T) { _ = ca.Stop(ctx) }) - ns, err := share.NewBlobNamespaceV0([]byte("namespace")) + ns, err := libshare.NewV0Namespace([]byte("namespace")) + require.NoError(t, err) + require.False(t, ns.IsReserved()) + require.NoError(t, err) - blobbyTheBlob, err := apptypes.NewBlob(ns.ToAppNamespace(), []byte("data"), 0) + blobbyTheBlob, err := libshare.NewV0Blob(ns, []byte("data")) require.NoError(t, err) testcases := []struct { name string - blobs []*squareblob.Blob + blobs []*libshare.Blob gasPrice float64 gasLim uint64 expErr error }{ { name: "empty blobs", - blobs: []*squareblob.Blob{}, + blobs: []*libshare.Blob{}, gasPrice: DefaultGasPrice, gasLim: 0, expErr: errors.New("state: no blobs provided"), }, { name: "good blob with user provided gas and fees", - blobs: []*squareblob.Blob{blobbyTheBlob}, + blobs: []*libshare.Blob{blobbyTheBlob}, gasPrice: 0.005, - gasLim: apptypes.DefaultEstimateGas([]uint32{uint32(len(blobbyTheBlob.GetData()))}), + gasLim: apptypes.DefaultEstimateGas([]uint32{uint32(blobbyTheBlob.DataLen())}), expErr: nil, }, // TODO: add more test cases. The problem right now is that the celestia-app doesn't @@ -148,6 +150,15 @@ func TestTransfer(t *testing.T) { } } +func TestChainIDMismatch(t *testing.T) { + ctx := context.Background() + ca, _ := buildAccessor(t) + ca.network = "mismatch" + // start the accessor + err := ca.Start(ctx) + assert.ErrorContains(t, err, "wrong network") +} + func TestDelegate(t *testing.T) { ctx := context.Background() ca, accounts := buildAccessor(t) @@ -253,7 +264,7 @@ func buildAccessor(t *testing.T) (*CoreAccessor, []string) { WithAppCreator(appCreator) // needed until https://github.com/celestiaorg/celestia-app/pull/3680 merges cctx, _, grpcAddr := testnode.NewNetwork(t, config) - ca, err := NewCoreAccessor(cctx.Keyring, accounts[0].Name, nil, "127.0.0.1", extractPort(grpcAddr)) + ca, err := NewCoreAccessor(cctx.Keyring, accounts[0].Name, nil, "127.0.0.1", extractPort(grpcAddr), chainID) require.NoError(t, err) return ca, getNames(accounts) } diff --git a/state/integration_test.go b/state/integration_test.go index 54c76fbcd3..8680b4d181 100644 --- a/state/integration_test.go +++ b/state/integration_test.go @@ -16,9 +16,9 @@ import ( rpcclient "github.com/tendermint/tendermint/rpc/client" "google.golang.org/grpc" - "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v2/test/util/genesis" - "github.com/celestiaorg/celestia-app/v2/test/util/testnode" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/test/util/genesis" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/core" @@ -52,7 +52,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().Greater(len(s.accounts), 0) accountName := s.accounts[0].Name - accessor, err := NewCoreAccessor(s.cctx.Keyring, accountName, localHeader{s.cctx.Client}, "", "") + accessor, err := NewCoreAccessor(s.cctx.Keyring, accountName, localHeader{s.cctx.Client}, "", "", "") require.NoError(s.T(), err) setClients(accessor, s.cctx.GRPCClient) s.accessor = accessor diff --git a/state/state.go b/state/state.go index 5f09bcc19b..d55bb6901c 100644 --- a/state/state.go +++ b/state/state.go @@ -7,8 +7,6 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" coretypes "github.com/tendermint/tendermint/types" - - squareblob "github.com/celestiaorg/go-square/blob" ) // Balance is an alias to the Coin type from Cosmos-SDK. @@ -26,9 +24,6 @@ type Address struct { sdk.Address } -// Blob is an alias of Blob from go-square. -type Blob = squareblob.Blob - // ValAddress is an alias to the ValAddress type from Cosmos-SDK. type ValAddress = sdk.ValAddress diff --git a/state/tx_config.go b/state/tx_config.go index 0c0ea5e372..179a33050a 100644 --- a/state/tx_config.go +++ b/state/tx_config.go @@ -8,8 +8,8 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keyring" sdktypes "github.com/cosmos/cosmos-sdk/types" - "github.com/celestiaorg/celestia-app/v2/pkg/user" - apptypes "github.com/celestiaorg/celestia-app/v2/x/blob/types" + "github.com/celestiaorg/celestia-app/v3/pkg/user" + apptypes "github.com/celestiaorg/celestia-app/v3/x/blob/types" ) const ( diff --git a/store/cache/accessor_cache.go b/store/cache/accessor_cache.go new file mode 100644 index 0000000000..893c73b7a9 --- /dev/null +++ b/store/cache/accessor_cache.go @@ -0,0 +1,264 @@ +package cache + +import ( + "context" + "fmt" + "runtime" + "sync" + "sync/atomic" + "time" + + lru "github.com/hashicorp/golang-lru/v2" + + "github.com/celestiaorg/celestia-node/share/eds" +) + +const defaultCloseTimeout = time.Minute + +var _ Cache = (*AccessorCache)(nil) + +// AccessorCache implements the Cache interface using an LRU cache backend. +type AccessorCache struct { + // The name is a prefix that will be used for cache metrics if they are enabled. + name string + // stripedLocks prevents simultaneous RW access to the accessor cache. Instead + // of using only one lock or one lock per uint64, we stripe the uint64s across 256 locks. 256 is + // chosen because it 0-255 is the range of values we get looking at the last byte of the uint64. + stripedLocks [256]*sync.RWMutex + // Caches the accessor for a given uint64 for accessor read affinity, i.e., further reads will + // likely be from the same accessor. Maps (Datahash -> accessor). + cache *lru.Cache[uint64, *accessor] + + metrics *metrics +} + +// accessor is the value stored in Cache. It implements the eds.AccessorStreamer interface. It has a +// reference counted so that it can be removed from the cache only when all references are released. +type accessor struct { + eds.AccessorStreamer + + lock sync.Mutex + done chan struct{} + refs atomic.Int32 + isClosed bool +} + +func NewAccessorCache(name string, cacheSize int) (*AccessorCache, error) { + bc := &AccessorCache{ + name: name, + stripedLocks: [256]*sync.RWMutex{}, + } + + for i := range bc.stripedLocks { + bc.stripedLocks[i] = &sync.RWMutex{} + } + // Instantiate the Accessor Cache. + bslru, err := lru.NewWithEvict[uint64, *accessor](cacheSize, bc.evictFn()) + if err != nil { + return nil, fmt.Errorf("creating accessor cache %s: %w", name, err) + } + bc.cache = bslru + return bc, nil +} + +// evictFn will be invoked when an item is evicted from the cache. +func (bc *AccessorCache) evictFn() func(uint64, *accessor) { + return func(_ uint64, ac *accessor) { + // we don't want to block cache on close and can release accessor from cache early, while it is + // being closed in parallel routine + go func() { + err := ac.close() + if err != nil { + bc.metrics.observeEvicted(true) + log.Errorf("couldn't close accessor after cache eviction: %s", err) + return + } + bc.metrics.observeEvicted(false) + }() + } +} + +// Has checks if accessor for the height is present on the AccessorCache. +func (bc *AccessorCache) Has(height uint64) bool { + lk := bc.getLock(height) + lk.RLock() + defer lk.RUnlock() + + return bc.cache.Contains(height) +} + +// Get retrieves the accessor for a given uint64 from the Cache. If the Accessor is not in +// the Cache, it returns an ErrCacheMiss. +func (bc *AccessorCache) Get(height uint64) (eds.AccessorStreamer, error) { + lk := bc.getLock(height) + lk.RLock() + defer lk.RUnlock() + + ac, ok := bc.cache.Get(height) + if !ok { + bc.metrics.observeGet(false) + return nil, ErrCacheMiss + } + + bc.metrics.observeGet(true) + return newRefCloser(ac) +} + +// GetOrLoad attempts to get an item from the cache, and if not found, invokes +// the provided loader function to load it. +func (bc *AccessorCache) GetOrLoad( + ctx context.Context, + height uint64, + loader OpenAccessorFn, +) (eds.AccessorStreamer, error) { + lk := bc.getLock(height) + lk.Lock() + defer lk.Unlock() + + ac, ok := bc.cache.Get(height) + if ok { + // return accessor, only if it is not closed yet + accessorWithRef, err := newRefCloser(ac) + if err == nil { + bc.metrics.observeGet(true) + return accessorWithRef, nil + } + } + + // accessor not found in cache or closed, so load new one using loader + f, err := loader(ctx) + if err != nil { + return nil, fmt.Errorf("unable to load accessor: %w", err) + } + + ac = &accessor{AccessorStreamer: f} + // Create a new accessor first to increment the reference count in it, so it cannot get evicted + // from the inner lru cache before it is used. + rc, err := newRefCloser(ac) + if err != nil { + return nil, err + } + bc.cache.Add(height, ac) + return rc, nil +} + +// Remove removes the Accessor for a given uint64 from the cache. +func (bc *AccessorCache) Remove(height uint64) error { + lk := bc.getLock(height) + lk.RLock() + ac, ok := bc.cache.Get(height) + lk.RUnlock() + if !ok { + // item is not in cache + return nil + } + if err := ac.close(); err != nil { + return err + } + // The cache will call evictFn on removal, where accessor close will be called. + bc.cache.Remove(height) + return nil +} + +// EnableMetrics enables metrics for the cache. +func (bc *AccessorCache) EnableMetrics() (unreg func() error, err error) { + if bc.metrics == nil { + bc.metrics, err = newMetrics(bc) + } + return bc.metrics.reg.Unregister, err +} + +func (s *accessor) addRef() error { + s.lock.Lock() + defer s.lock.Unlock() + if s.isClosed { + // item is already closed and soon will be removed after all refs are released + return ErrCacheMiss + } + if s.refs.Add(1) == 1 { + // there were no refs previously and done channel was closed, reopen it by recreating + s.done = make(chan struct{}) + } + return nil +} + +func (s *accessor) removeRef() { + s.lock.Lock() + defer s.lock.Unlock() + if s.refs.Add(-1) <= 0 { + close(s.done) + } +} + +// close closes the accessor and removes it from the cache if it is not closed yet. It will block +// until all references are released or timeout is reached. +func (s *accessor) close() error { + s.lock.Lock() + if s.isClosed { + s.lock.Unlock() + // accessor will be closed by another goroutine + return nil + } + s.isClosed = true + done := s.done + s.lock.Unlock() + + // wait until all references are released or timeout is reached. If timeout is reached, log an + // error and close the accessor forcefully. + select { + case <-done: + case <-time.After(defaultCloseTimeout): + log.Errorf("closing accessor, some readers didn't close the accessor within timeout,"+ + " amount left: %v", s.refs.Load()) + } + if err := s.AccessorStreamer.Close(); err != nil { + return fmt.Errorf("closing accessor: %w", err) + } + return nil +} + +// refCloser exists for reference counting protection on accessor. It ensures that a caller can't +// decrement it more than once. +type refCloser struct { + *accessor + closed atomic.Bool + removeRef func() +} + +// newRefCloser creates new refCloser +func newRefCloser(abs *accessor) (*refCloser, error) { + if err := abs.addRef(); err != nil { + return nil, err + } + + rf := &refCloser{ + accessor: abs, + removeRef: abs.removeRef, + } + // Set finalizer to ensure that accessor is closed when refCloser is garbage collected. + // We expect that refCloser will be closed explicitly by the caller. If it is not closed, + // we log an error. + runtime.SetFinalizer(rf, func(rf *refCloser) { + if rf.close() { + log.Errorf("refCloser for accessor was garbage collected before Close was called") + } + }) + return rf, nil +} + +func (c *refCloser) close() bool { + if c.closed.CompareAndSwap(false, true) { + c.removeRef() + return true + } + return false +} + +func (c *refCloser) Close() error { + c.close() + return nil +} + +func (bc *AccessorCache) getLock(k uint64) *sync.RWMutex { + return bc.stripedLocks[byte(k%256)] +} diff --git a/share/eds/cache/accessor_cache_test.go b/store/cache/accessor_cache_test.go similarity index 60% rename from share/eds/cache/accessor_cache_test.go rename to store/cache/accessor_cache_test.go index 347b251a88..8b537049e1 100644 --- a/share/eds/cache/accessor_cache_test.go +++ b/store/cache/accessor_cache_test.go @@ -9,71 +9,75 @@ import ( "testing" "time" - "github.com/filecoin-project/dagstore" - "github.com/filecoin-project/dagstore/shard" - blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" ) func TestAccessorCache(t *testing.T) { - t.Run("add / get item from cache", func(t *testing.T) { + t.Run("add / has / get item from cache", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() cache, err := NewAccessorCache("test", 1) require.NoError(t, err) // add accessor to the cache - key := shard.KeyFromString("key") + height := uint64(1) mock := &mockAccessor{ data: []byte("test_data"), } - loaded, err := cache.GetOrLoad(ctx, key, func(ctx context.Context, key shard.Key) (Accessor, error) { + loaded, err := cache.GetOrLoad(ctx, height, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) + reader, err := loaded.Reader() + require.NoError(t, err) + data, err := io.ReadAll(reader) + require.NoError(t, err) + require.Equal(t, mock.data, data) + err = loaded.Close() + require.NoError(t, err) // check if item exists - got, err := cache.Get(key) + has := cache.Has(height) + require.True(t, has) + got, err := cache.Get(height) require.NoError(t, err) - - l, err := io.ReadAll(loaded.Reader()) + reader, err = got.Reader() + require.NoError(t, err) + data, err = io.ReadAll(reader) require.NoError(t, err) - require.Equal(t, mock.data, l) - g, err := io.ReadAll(got.Reader()) + require.Equal(t, mock.data, data) + err = got.Close() require.NoError(t, err) - require.Equal(t, mock.data, g) }) - t.Run("get blockstore from accessor", func(t *testing.T) { + t.Run("get reader from accessor", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() cache, err := NewAccessorCache("test", 1) require.NoError(t, err) // add accessor to the cache - key := shard.KeyFromString("key") + height := uint64(1) mock := &mockAccessor{} - accessor, err := cache.GetOrLoad(ctx, key, func(ctx context.Context, key shard.Key) (Accessor, error) { + accessor, err := cache.GetOrLoad(ctx, height, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) // check if item exists - _, err = cache.Get(key) + _, err = cache.Get(height) require.NoError(t, err) - // blockstore should be created only after first request - require.Equal(t, 0, mock.returnedBs) - - // try to get blockstore - _, err = accessor.Blockstore() - require.NoError(t, err) - - // second call to blockstore should return same blockstore - _, err = accessor.Blockstore() + // try to get reader + _, err = accessor.Reader() require.NoError(t, err) - require.Equal(t, 1, mock.returnedBs) }) t.Run("remove an item", func(t *testing.T) { @@ -83,24 +87,26 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) // add accessor to the cache - key := shard.KeyFromString("key") + height := uint64(1) mock := &mockAccessor{} - ac, err := cache.GetOrLoad(ctx, key, func(ctx context.Context, key shard.Key) (Accessor, error) { + ac, err := cache.GetOrLoad(ctx, height, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) err = ac.Close() require.NoError(t, err) - err = cache.Remove(key) + err = cache.Remove(height) require.NoError(t, err) // accessor should be closed on removal mock.checkClosed(t, true) // check if item exists - _, err = cache.Get(key) - require.ErrorIs(t, err, errCacheMiss) + has := cache.Has(height) + require.False(t, has) + _, err = cache.Get(height) + require.ErrorIs(t, err, ErrCacheMiss) }) t.Run("successive reads should read the same data", func(t *testing.T) { @@ -110,23 +116,27 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) // add accessor to the cache - key := shard.KeyFromString("key") + height := uint64(1) mock := &mockAccessor{data: []byte("test")} - accessor, err := cache.GetOrLoad(ctx, key, func(ctx context.Context, key shard.Key) (Accessor, error) { + accessor, err := cache.GetOrLoad(ctx, height, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) - loaded, err := io.ReadAll(accessor.Reader()) + reader, err := accessor.Reader() require.NoError(t, err) - require.Equal(t, mock.data, loaded) + data, err := io.ReadAll(reader) + require.NoError(t, err) + require.Equal(t, mock.data, data) for i := 0; i < 2; i++ { - accessor, err = cache.Get(key) + accessor, err = cache.Get(height) + require.NoError(t, err) + reader, err := accessor.Reader() require.NoError(t, err) - got, err := io.ReadAll(accessor.Reader()) + data, err := io.ReadAll(reader) require.NoError(t, err) - require.Equal(t, mock.data, got) + require.Equal(t, mock.data, data) } }) @@ -137,9 +147,9 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) // add accessor to the cache - key := shard.KeyFromString("key") + height := uint64(1) mock := &mockAccessor{} - ac1, err := cache.GetOrLoad(ctx, key, func(ctx context.Context, key shard.Key) (Accessor, error) { + ac1, err := cache.GetOrLoad(ctx, height, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) @@ -147,8 +157,8 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) // add second item - key2 := shard.KeyFromString("key2") - ac2, err := cache.GetOrLoad(ctx, key2, func(ctx context.Context, key shard.Key) (Accessor, error) { + height2 := uint64(2) + ac2, err := cache.GetOrLoad(ctx, height2, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) @@ -158,9 +168,11 @@ func TestAccessorCache(t *testing.T) { // accessor should be closed on removal by eviction mock.checkClosed(t, true) - // check if item evicted - _, err = cache.Get(key) - require.ErrorIs(t, err, errCacheMiss) + // first item should be evicted from cache + has := cache.Has(height) + require.False(t, has) + _, err = cache.Get(height) + require.ErrorIs(t, err, ErrCacheMiss) }) t.Run("close on accessor is not closing underlying accessor", func(t *testing.T) { @@ -170,15 +182,15 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) // add accessor to the cache - key := shard.KeyFromString("key") + height := uint64(1) mock := &mockAccessor{} - _, err = cache.GetOrLoad(ctx, key, func(ctx context.Context, key shard.Key) (Accessor, error) { + _, err = cache.GetOrLoad(ctx, height, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) // check if item exists - accessor, err := cache.Get(key) + accessor, err := cache.Get(height) require.NoError(t, err) require.NotNil(t, accessor) @@ -197,21 +209,21 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) // add accessor to the cache - key := shard.KeyFromString("key") + height := uint64(1) mock := &mockAccessor{} - accessor1, err := cache.GetOrLoad(ctx, key, func(ctx context.Context, key shard.Key) (Accessor, error) { + accessor1, err := cache.GetOrLoad(ctx, height, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock, nil }) require.NoError(t, err) // create second readers - accessor2, err := cache.Get(key) + accessor2, err := cache.Get(height) require.NoError(t, err) // initialize close done := make(chan struct{}) go func() { - err := cache.Remove(key) + err := cache.Remove(height) require.NoError(t, err) close(done) }() @@ -226,9 +238,9 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) mock.checkClosed(t, false) - // reads for item that is being evicted should result in errCacheMiss - _, err = cache.Get(key) - require.ErrorIs(t, err, errCacheMiss) + // reads for item that is being evicted should result in ErrCacheMiss + _, err = cache.Get(height) + require.ErrorIs(t, err, ErrCacheMiss) // close second reader and wait for accessor to be closed err = accessor2.Close() @@ -251,24 +263,24 @@ func TestAccessorCache(t *testing.T) { require.NoError(t, err) // add accessor to the cache - key1 := shard.KeyFromString("key1") + height1 := uint64(1) mock1 := &mockAccessor{} - accessor1, err := cache.GetOrLoad(ctx, key1, func(ctx context.Context, key shard.Key) (Accessor, error) { + accessor1, err := cache.GetOrLoad(ctx, height1, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock1, nil }) require.NoError(t, err) // add second accessor, to trigger eviction of the first one - key2 := shard.KeyFromString("key2") + height2 := uint64(2) mock2 := &mockAccessor{} - accessor2, err := cache.GetOrLoad(ctx, key2, func(ctx context.Context, key shard.Key) (Accessor, error) { + accessor2, err := cache.GetOrLoad(ctx, height2, func(ctx context.Context) (eds.AccessorStreamer, error) { return mock2, nil }) require.NoError(t, err) // first accessor should be evicted from cache - _, err = cache.Get(key1) - require.ErrorIs(t, err, errCacheMiss) + _, err = cache.Get(height1) + require.ErrorIs(t, err, ErrCacheMiss) // first accessor should not be closed before all refs are released by Close() is calls. mock1.checkClosed(t, false) @@ -286,26 +298,43 @@ func TestAccessorCache(t *testing.T) { } type mockAccessor struct { - m sync.Mutex - data []byte - isClosed bool - returnedBs int + m sync.Mutex + data []byte + isClosed bool } -func (m *mockAccessor) Reader() io.Reader { - m.m.Lock() - defer m.m.Unlock() - return bytes.NewBuffer(m.data) +func (m *mockAccessor) Size(context.Context) int { + panic("implement me") +} + +func (m *mockAccessor) DataHash(context.Context) (share.DataHash, error) { + panic("implement me") +} + +func (m *mockAccessor) AxisRoots(context.Context) (*share.AxisRoots, error) { + panic("implement me") +} + +func (m *mockAccessor) Sample(context.Context, shwap.SampleCoords) (shwap.Sample, error) { + panic("implement me") +} + +func (m *mockAccessor) AxisHalf(context.Context, rsmt2d.Axis, int) (eds.AxisHalf, error) { + panic("implement me") } -func (m *mockAccessor) Blockstore() (dagstore.ReadBlockstore, error) { +func (m *mockAccessor) RowNamespaceData(context.Context, libshare.Namespace, int) (shwap.RowNamespaceData, error) { + panic("implement me") +} + +func (m *mockAccessor) Shares(context.Context) ([]libshare.Share, error) { + panic("implement me") +} + +func (m *mockAccessor) Reader() (io.Reader, error) { m.m.Lock() defer m.m.Unlock() - if m.returnedBs > 0 { - return nil, errors.New("blockstore already returned") - } - m.returnedBs++ - return rbsMock{}, nil + return bytes.NewBuffer(m.data), nil } func (m *mockAccessor) Close() error { @@ -319,32 +348,9 @@ func (m *mockAccessor) Close() error { } func (m *mockAccessor) checkClosed(t *testing.T, expected bool) { - // item will be removed in background, so give it some time to settle + // item will be removed async in background, give it some time to settle time.Sleep(time.Millisecond * 100) m.m.Lock() defer m.m.Unlock() require.Equal(t, expected, m.isClosed) } - -// rbsMock is a dagstore.ReadBlockstore mock -type rbsMock struct{} - -func (r rbsMock) Has(context.Context, cid.Cid) (bool, error) { - panic("implement me") -} - -func (r rbsMock) Get(_ context.Context, _ cid.Cid) (blocks.Block, error) { - panic("implement me") -} - -func (r rbsMock) GetSize(context.Context, cid.Cid) (int, error) { - panic("implement me") -} - -func (r rbsMock) AllKeysChan(context.Context) (<-chan cid.Cid, error) { - panic("implement me") -} - -func (r rbsMock) HashOnRead(bool) { - panic("implement me") -} diff --git a/store/cache/cache.go b/store/cache/cache.go new file mode 100644 index 0000000000..10e7dffcb3 --- /dev/null +++ b/store/cache/cache.go @@ -0,0 +1,39 @@ +package cache + +import ( + "context" + "errors" + + logging "github.com/ipfs/go-log/v2" + "go.opentelemetry.io/otel" + + "github.com/celestiaorg/celestia-node/share/eds" +) + +var ( + log = logging.Logger("store/cache") + meter = otel.Meter("store_cache") +) + +var ErrCacheMiss = errors.New("accessor not found in cache") + +type OpenAccessorFn func(context.Context) (eds.AccessorStreamer, error) + +// Cache is an interface that defines the basic Cache operations. +type Cache interface { + // Has checks if the Cache contains the eds.AccessorStreamer for the given height. + Has(height uint64) bool + + // Get returns the eds.AccessorStreamer for the given height. + Get(height uint64) (eds.AccessorStreamer, error) + + // GetOrLoad attempts to get an item from the Cache and, if not found, invokes + // the provided loader function to load it into the Cache. + GetOrLoad(ctx context.Context, height uint64, open OpenAccessorFn) (eds.AccessorStreamer, error) + + // Remove removes an item from Cache. + Remove(height uint64) error + + // EnableMetrics enables metrics in Cache + EnableMetrics() (unreg func() error, err error) +} diff --git a/store/cache/doublecache.go b/store/cache/doublecache.go new file mode 100644 index 0000000000..d0fb2c47b9 --- /dev/null +++ b/store/cache/doublecache.go @@ -0,0 +1,83 @@ +package cache + +import ( + "context" + "errors" + "fmt" + + "github.com/celestiaorg/celestia-node/share/eds" +) + +// DoubleCache represents a Cache that looks into multiple caches one by one. +type DoubleCache struct { + first, second Cache +} + +// NewDoubleCache creates a new DoubleCache with the provided caches. +func NewDoubleCache(first, second Cache) *DoubleCache { + return &DoubleCache{ + first: first, + second: second, + } +} + +// Has checks if accessor for the height is present on the AccessorCache. +func (mc *DoubleCache) Has(height uint64) bool { + return mc.first.Has(height) || mc.second.Has(height) +} + +// Get looks for an item in all the caches one by one and returns the Cache found item. +func (mc *DoubleCache) Get(height uint64) (eds.AccessorStreamer, error) { + accessor, err := mc.first.Get(height) + if err == nil { + return accessor, nil + } + return mc.second.Get(height) +} + +// GetOrLoad attempts to get an item from the both caches and, if not found, invokes +// the provided loader function to load it into the first Cache. +func (mc *DoubleCache) GetOrLoad( + ctx context.Context, + height uint64, + loader OpenAccessorFn, +) (eds.AccessorStreamer, error) { + // look-up in second cache first + accessor, err := mc.second.Get(height) + if err == nil { + return accessor, nil + } + // not found in second, get or load from first one + return mc.first.GetOrLoad(ctx, height, loader) +} + +// Remove removes an item from all underlying caches +func (mc *DoubleCache) Remove(height uint64) error { + err1 := mc.first.Remove(height) + err2 := mc.second.Remove(height) + return errors.Join(err1, err2) +} + +func (mc *DoubleCache) First() Cache { + return mc.first +} + +func (mc *DoubleCache) Second() Cache { + return mc.second +} + +func (mc *DoubleCache) EnableMetrics() (unreg func() error, err error) { + unreg1, err := mc.first.EnableMetrics() + if err != nil { + return nil, fmt.Errorf("while enabling metrics for first cache: %w", err) + } + unreg2, err := mc.second.EnableMetrics() + if err != nil { + return unreg1, fmt.Errorf("while enabling metrics for second cache: %w", err) + } + + unregFn := func() error { + return errors.Join(unreg1(), unreg2()) + } + return unregFn, nil +} diff --git a/share/eds/cache/metrics.go b/store/cache/metrics.go similarity index 58% rename from share/eds/cache/metrics.go rename to store/cache/metrics.go index 701b7e3a71..a43ae031ff 100644 --- a/share/eds/cache/metrics.go +++ b/store/cache/metrics.go @@ -2,6 +2,7 @@ package cache import ( "context" + "fmt" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -15,27 +16,26 @@ const ( type metrics struct { getCounter metric.Int64Counter evictedCounter metric.Int64Counter - - clientReg metric.Registration + reg metric.Registration } func newMetrics(bc *AccessorCache) (*metrics, error) { - metricsPrefix := "eds_blockstore_cache_" + bc.name + metricsPrefix := fmt.Sprintf("eds_cache_%p_", bc) - evictedCounter, err := meter.Int64Counter(metricsPrefix+"_evicted_counter", - metric.WithDescription("eds blockstore cache evicted event counter")) + evictedCounter, err := meter.Int64Counter(metricsPrefix+"evicted_counter", + metric.WithDescription("eds cache evicted event counter")) if err != nil { return nil, err } - getCounter, err := meter.Int64Counter(metricsPrefix+"_get_counter", - metric.WithDescription("eds blockstore cache evicted event counter")) + getCounter, err := meter.Int64Counter(metricsPrefix+"get_counter", + metric.WithDescription("eds cache get event counter")) if err != nil { return nil, err } - cacheSize, err := meter.Int64ObservableGauge(metricsPrefix+"_size", - metric.WithDescription("total amount of items in blockstore cache"), + cacheSize, err := meter.Int64ObservableGauge(metricsPrefix+"size", + metric.WithDescription("total amount of items in cache"), ) if err != nil { return nil, err @@ -45,23 +45,13 @@ func newMetrics(bc *AccessorCache) (*metrics, error) { observer.ObserveInt64(cacheSize, int64(bc.cache.Len())) return nil } - clientReg, err := meter.RegisterCallback(callback, cacheSize) - if err != nil { - return nil, err - } + reg, err := meter.RegisterCallback(callback, cacheSize) return &metrics{ getCounter: getCounter, evictedCounter: evictedCounter, - clientReg: clientReg, - }, nil -} - -func (m *metrics) close() error { - if m == nil { - return nil - } - return m.clientReg.Unregister() + reg: reg, + }, err } func (m *metrics) observeEvicted(failed bool) { diff --git a/store/cache/noop.go b/store/cache/noop.go new file mode 100644 index 0000000000..27b33e2dd6 --- /dev/null +++ b/store/cache/noop.go @@ -0,0 +1,86 @@ +package cache + +import ( + "context" + "io" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ Cache = (*NoopCache)(nil) + +// NoopCache implements noop version of Cache interface +type NoopCache struct{} + +func (n NoopCache) Has(uint64) bool { + return false +} + +func (n NoopCache) Get(uint64) (eds.AccessorStreamer, error) { + return nil, ErrCacheMiss +} + +func (n NoopCache) GetOrLoad(ctx context.Context, _ uint64, loader OpenAccessorFn) (eds.AccessorStreamer, error) { + return loader(ctx) +} + +func (n NoopCache) Remove(uint64) error { + return nil +} + +func (n NoopCache) EnableMetrics() (unreg func() error, err error) { + noop := func() error { return nil } + return noop, nil +} + +var _ eds.AccessorStreamer = NoopFile{} + +// NoopFile implements noop version of eds.AccessorStreamer interface +type NoopFile struct{} + +func (n NoopFile) Reader() (io.Reader, error) { + return noopReader{}, nil +} + +func (n NoopFile) Size(context.Context) int { + return 0 +} + +func (n NoopFile) DataHash(context.Context) (share.DataHash, error) { + return share.DataHash{}, nil +} + +func (n NoopFile) AxisRoots(context.Context) (*share.AxisRoots, error) { + return &share.AxisRoots{}, nil +} + +func (n NoopFile) Sample(context.Context, shwap.SampleCoords) (shwap.Sample, error) { + return shwap.Sample{}, nil +} + +func (n NoopFile) AxisHalf(context.Context, rsmt2d.Axis, int) (eds.AxisHalf, error) { + return eds.AxisHalf{}, nil +} + +func (n NoopFile) RowNamespaceData(context.Context, libshare.Namespace, int) (shwap.RowNamespaceData, error) { + return shwap.RowNamespaceData{}, nil +} + +func (n NoopFile) Shares(context.Context) ([]libshare.Share, error) { + return []libshare.Share{}, nil +} + +func (n NoopFile) Close() error { + return nil +} + +type noopReader struct{} + +func (n noopReader) Read([]byte) (int, error) { + return 0, nil +} diff --git a/store/file/codec.go b/store/file/codec.go new file mode 100644 index 0000000000..f8e7b91ec9 --- /dev/null +++ b/store/file/codec.go @@ -0,0 +1,38 @@ +package file + +import ( + "sync" + + "github.com/klauspost/reedsolomon" +) + +var codec Codec + +func init() { + codec = NewCodec() +} + +type Codec interface { + Encoder(ln int) (reedsolomon.Encoder, error) +} + +type codecCache struct { + cache sync.Map +} + +func NewCodec() Codec { + return &codecCache{} +} + +func (l *codecCache) Encoder(ln int) (reedsolomon.Encoder, error) { + enc, ok := l.cache.Load(ln) + if !ok { + var err error + enc, err = reedsolomon.New(ln/2, ln/2, reedsolomon.WithLeopardGF(true)) + if err != nil { + return nil, err + } + l.cache.Store(ln, enc) + } + return enc.(reedsolomon.Encoder), nil +} diff --git a/store/file/codec_test.go b/store/file/codec_test.go new file mode 100644 index 0000000000..eabb125c8e --- /dev/null +++ b/store/file/codec_test.go @@ -0,0 +1,84 @@ +package file + +import ( + "fmt" + "testing" + + "github.com/klauspost/reedsolomon" + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" +) + +func BenchmarkCodec(b *testing.B) { + minSize, maxSize := 32, 128 + + for size := minSize; size <= maxSize; size *= 2 { + // BenchmarkCodec/Leopard/size:32-10 409194 2793 ns/op + // BenchmarkCodec/Leopard/size:64-10 190969 6170 ns/op + // BenchmarkCodec/Leopard/size:128-10 82821 14287 ns/op + b.Run(fmt.Sprintf("Leopard/size:%v", size), func(b *testing.B) { + enc, err := reedsolomon.New(size/2, size/2, reedsolomon.WithLeopardGF(true)) + require.NoError(b, err) + + shards := newShards(b, size, true) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err = enc.Encode(shards) + require.NoError(b, err) + } + }) + + // BenchmarkCodec/default/size:32-10 222153 5364 ns/op + // BenchmarkCodec/default/size:64-10 58831 20349 ns/op + // BenchmarkCodec/default/size:128-10 14940 80471 ns/op + b.Run(fmt.Sprintf("default/size:%v", size), func(b *testing.B) { + enc, err := reedsolomon.New(size/2, size/2, reedsolomon.WithLeopardGF(false)) + require.NoError(b, err) + + shards := newShards(b, size, true) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err = enc.Encode(shards) + require.NoError(b, err) + } + }) + + // BenchmarkCodec/default-reconstructSome/size:32-10 1263585 954.4 ns/op + // BenchmarkCodec/default-reconstructSome/size:64-10 762273 1554 ns/op + // BenchmarkCodec/default-reconstructSome/size:128-10 429268 2974 ns/op + b.Run(fmt.Sprintf("default-reconstructSome/size:%v", size), func(b *testing.B) { + enc, err := reedsolomon.New(size/2, size/2, reedsolomon.WithLeopardGF(false)) + require.NoError(b, err) + + shards := newShards(b, size, false) + targets := make([]bool, size) + target := size - 2 + targets[target] = true + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err = enc.ReconstructSome(shards, targets) + require.NoError(b, err) + shards[target] = nil + } + }) + } +} + +func newShards(b testing.TB, size int, fillParity bool) [][]byte { + shards := make([][]byte, size) + original, err := libshare.RandShares(size / 2) + require.NoError(b, err) + copy(shards, libshare.ToBytes(original)) + + if fillParity { + // fill with parity empty Shares + for j := len(original); j < len(shards); j++ { + shards[j] = make([]byte, libshare.ShareSize) + } + } + return shards +} diff --git a/store/file/file.go b/store/file/file.go new file mode 100644 index 0000000000..3cf89015f6 --- /dev/null +++ b/store/file/file.go @@ -0,0 +1,14 @@ +package file + +import ( + logging "github.com/ipfs/go-log/v2" +) + +var log = logging.Logger("store/file") + +const ( + // writeBufferSize defines buffer size for optimized batched writes into the file system. + // TODO(@Wondertan): Consider making it configurable + writeBufferSize = 64 << 10 + filePermissions = 0o600 +) diff --git a/store/file/header.go b/store/file/header.go new file mode 100644 index 0000000000..7c2d8c7924 --- /dev/null +++ b/store/file/header.go @@ -0,0 +1,107 @@ +package file + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/celestiaorg/celestia-node/share" +) + +// headerVOSize is the size of the headerV0 in bytes. It has more space than the headerV0 struct +// to allow for future extensions of the header without breaking compatibility. +const headerVOSize int = 64 + +type headerVersion uint8 + +const headerVersionV0 headerVersion = iota + 1 + +type headerV0 struct { + fileVersion fileVersion + + // Taken directly from EDS + shareSize uint16 + squareSize uint16 + + datahash share.DataHash +} + +type fileVersion uint8 + +const ( + fileV0 fileVersion = iota + 1 +) + +func readHeader(r io.Reader) (*headerV0, error) { + // read first byte to determine the fileVersion + var version headerVersion + err := binary.Read(r, binary.LittleEndian, &version) + if err != nil { + return nil, fmt.Errorf("readHeader: %w", err) + } + + switch version { + case headerVersionV0: + h := &headerV0{} + _, err := h.ReadFrom(r) + return h, err + default: + return nil, fmt.Errorf("unsupported header fileVersion: %d", version) + } +} + +func writeHeader(w io.Writer, h *headerV0) error { + err := binary.Write(w, binary.LittleEndian, headerVersionV0) + if err != nil { + return fmt.Errorf("writeHeader: %w", err) + } + _, err = h.WriteTo(w) + return err +} + +func (h *headerV0) SquareSize() int { + return int(h.squareSize) +} + +func (h *headerV0) ShareSize() int { + return int(h.shareSize) +} + +func (h *headerV0) Size() int { + // header size + 1 byte for header fileVersion + return headerVOSize + 1 +} + +func (h *headerV0) RootsSize() int { + // axis roots are stored in two parts: row roots and column roots, each part has size equal to + // the square size. Thus, the total amount of roots is equal to the square size * 2. + return share.AxisRootSize * h.SquareSize() * 2 +} + +func (h *headerV0) OffsetWithRoots() int { + return h.RootsSize() + h.Size() +} + +func (h *headerV0) WriteTo(w io.Writer) (int64, error) { + buf := make([]byte, headerVOSize) + buf[0] = byte(h.fileVersion) + binary.LittleEndian.PutUint16(buf[28:30], h.shareSize) + binary.LittleEndian.PutUint16(buf[30:32], h.squareSize) + copy(buf[32:64], h.datahash) + n, err := w.Write(buf) + return int64(n), err +} + +func (h *headerV0) ReadFrom(r io.Reader) (int64, error) { + bytesHeader := make([]byte, headerVOSize) + n, err := io.ReadFull(r, bytesHeader) + if n != headerVOSize { + return 0, fmt.Errorf("headerV0 ReadFrom: read %d bytes, expected %d", len(bytesHeader), headerVOSize) + } + + h.fileVersion = fileVersion(bytesHeader[0]) + h.shareSize = binary.LittleEndian.Uint16(bytesHeader[28:30]) + h.squareSize = binary.LittleEndian.Uint16(bytesHeader[30:32]) + h.datahash = bytesHeader[32:64] + return int64(headerVOSize), err +} diff --git a/store/file/header_test.go b/store/file/header_test.go new file mode 100644 index 0000000000..25987ac13e --- /dev/null +++ b/store/file/header_test.go @@ -0,0 +1,50 @@ +package file + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" +) + +// Due to bug on macOS we need to specify additional linker flags: +// go test -v -run=^$ -fuzz=Fuzz_writeReadheader -ldflags=-extldflags=-Wl,-ld_classic . + +func Fuzz_writeReadheader(f *testing.F) { + f.Add(uint8(0), uint8(0), uint16(0), uint16(0), []byte{31: 0}) + f.Add(uint8(1), uint8(1), uint16(1), uint16(1), []byte{31: 0}) + f.Add(uint8(1), uint8(1), uint16(1), uint16(1), []byte{100: 1}) + + f.Fuzz(func(t *testing.T, ver, typ uint8, shs, sqs uint16, b []byte) { + // we expect hash to be 32 bytes, crop or extend to 32 bytes. + diff := len(b) - 32 + if diff > 0 { + b = b[:32] + } else { + pad := bytes.Repeat([]byte{0}, -diff) + b = append(b, pad...) + } + + testHdr := headerV0{ + fileVersion: fileVersion(ver), + shareSize: shs, + squareSize: sqs, + datahash: b, + } + + w := bytes.NewBuffer(nil) + err := writeHeader(w, &testHdr) + if err != nil { + return + } + + hdr, err := readHeader(w) + if err != nil { + return + } + + require.Equal(t, hdr.shareSize, testHdr.shareSize) + require.Equal(t, hdr.squareSize, testHdr.squareSize) + require.Equal(t, hdr.datahash, testHdr.datahash) + }) +} diff --git a/store/file/ods.go b/store/file/ods.go new file mode 100644 index 0000000000..e9b14e8a80 --- /dev/null +++ b/store/file/ods.go @@ -0,0 +1,477 @@ +package file + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "os" + "sync" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ eds.AccessorStreamer = (*ODS)(nil) + +// ODS implements eds.Accessor as an FS file. +// It stores the original data square(ODS), which is the first quadrant of EDS, +// and it's metadata in file's header. +type ODS struct { + hdr *headerV0 + fl *os.File + + lock sync.RWMutex + // ods stores an in-memory cache of the original data square to enhance read performance. This + // cache is particularly beneficial for operations that require reading the entire square, such as: + // - Serving samples from the fourth quadrant of the square, which necessitates reconstructing data + // from all rows. - Streaming the entire ODS by Reader(), ensuring efficient data delivery without + // repeated file reads. - Serving full ODS data by Shares(). + // Storing the square in memory allows for efficient single-read operations, avoiding the need for + // piecemeal reads by rows or columns, and facilitates quick access to data for these operations. + ods square + // disableCache is a flag that, when set to true, disables the in-memory cache of the original data + // Used for testing and benchmarking purposes, this flag allows for the evaluation of the + // performance. + disableCache bool +} + +// CreateODS creates a new file under given FS path and +// writes the ODS into it out of given EDS. +// It may leave partially written file if any of the writes fail. +func CreateODS( + path string, + roots *share.AxisRoots, + eds *rsmt2d.ExtendedDataSquare, +) error { + mod := os.O_RDWR | os.O_CREATE | os.O_EXCL // ensure we fail if already exist + f, err := os.OpenFile(path, mod, filePermissions) + if err != nil { + return fmt.Errorf("creating ODS file: %w", err) + } + + shareSize := len(eds.GetCell(0, 0)) + hdr := &headerV0{ + fileVersion: fileV0, + shareSize: uint16(shareSize), + squareSize: uint16(eds.Width()), + datahash: roots.Hash(), + } + + err = writeODSFile(f, roots, eds, hdr) + if errClose := f.Close(); errClose != nil { + err = errors.Join(err, fmt.Errorf("closing created ODS file: %w", errClose)) + } + + return err +} + +// writeQ4File full ODS content into OS File. +func writeODSFile(f *os.File, axisRoots *share.AxisRoots, eds *rsmt2d.ExtendedDataSquare, hdr *headerV0) error { + // buffering gives us ~4x speed up + buf := bufio.NewWriterSize(f, writeBufferSize) + + if err := writeHeader(f, hdr); err != nil { + return fmt.Errorf("writing header: %w", err) + } + + if err := writeAxisRoots(buf, axisRoots); err != nil { + return fmt.Errorf("writing axis roots: %w", err) + } + + if err := writeODS(buf, eds); err != nil { + return fmt.Errorf("writing ODS: %w", err) + } + + if err := buf.Flush(); err != nil { + return fmt.Errorf("flushing ODS file: %w", err) + } + + return nil +} + +// writeODS writes the first quadrant(ODS) of the square to the writer. It writes the quadrant in +// row-major order. Write finishes once all the shares are written or on the first instance of tail +// padding share. Tail padding share are constant and aren't stored. +func writeODS(w io.Writer, eds *rsmt2d.ExtendedDataSquare) error { + for i := range eds.Width() / 2 { + for j := range eds.Width() / 2 { + shr := eds.GetCell(i, j) // TODO: Avoid copying inside GetCell + ns, err := libshare.NewNamespaceFromBytes(shr[:libshare.NamespaceSize]) + if err != nil { + return fmt.Errorf("creating namespace: %w", err) + } + if ns.Equals(libshare.TailPaddingNamespace) { + return nil + } + + _, err = w.Write(shr) + if err != nil { + return fmt.Errorf("writing share: %w", err) + } + } + } + return nil +} + +// writeAxisRoots writes RowRoots followed by ColumnRoots. +func writeAxisRoots(w io.Writer, roots *share.AxisRoots) error { + for _, root := range roots.RowRoots { + if _, err := w.Write(root); err != nil { + return fmt.Errorf("writing row roots: %w", err) + } + } + + for _, root := range roots.ColumnRoots { + if _, err := w.Write(root); err != nil { + return fmt.Errorf("writing columm roots: %w", err) + } + } + + return nil +} + +// ValidateODSSize checks if the file under given FS path has the expected size. +func ValidateODSSize(path string, eds *rsmt2d.ExtendedDataSquare) error { + ods, err := OpenODS(path) + if err != nil { + return fmt.Errorf("opening file: %w", err) + } + + shares, err := filledSharesAmount(eds) + if err != nil { + return fmt.Errorf("calculating shares amount: %w", err) + } + shareSize := len(eds.GetCell(0, 0)) + expectedSize := ods.hdr.OffsetWithRoots() + shares*shareSize + + info, err := ods.fl.Stat() + if err != nil { + return fmt.Errorf("getting file info: %w", err) + } + if info.Size() != int64(expectedSize) { + return fmt.Errorf("file size mismatch: expected %d, got %d", expectedSize, info.Size()) + } + return nil +} + +// OpenODS opens an existing ODS file under given FS path. +// It only reads the header with metadata. The other content +// of the File is read lazily. +// If file is empty, the ErrEmptyFile is returned. +// File must be closed after usage. +func OpenODS(path string) (*ODS, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + + h, err := readHeader(f) + if err != nil { + return nil, err + } + + return &ODS{ + hdr: h, + fl: f, + }, nil +} + +// Size returns EDS size stored in file's header. +func (o *ODS) Size(context.Context) int { + return o.size() +} + +func (o *ODS) size() int { + return int(o.hdr.squareSize) +} + +// DataHash returns root hash of Accessor's underlying EDS. +func (o *ODS) DataHash(context.Context) (share.DataHash, error) { + return o.hdr.datahash, nil +} + +// AxisRoots reads AxisRoots stored in the file. AxisRoots are stored after the header and before +// the ODS data. +func (o *ODS) AxisRoots(context.Context) (*share.AxisRoots, error) { + roots := make([]byte, o.hdr.RootsSize()) + n, err := o.fl.ReadAt(roots, int64(o.hdr.Size())) + if err != nil { + return nil, fmt.Errorf("reading axis roots: %w", err) + } + if n != len(roots) { + return nil, fmt.Errorf("reading axis roots: expected %d bytes, got %d", len(roots), n) + } + rowRoots := make([][]byte, o.size()) + colRoots := make([][]byte, o.size()) + for i := 0; i < o.size(); i++ { + rowRoots[i] = roots[i*share.AxisRootSize : (i+1)*share.AxisRootSize] + colRoots[i] = roots[(o.size()+i)*share.AxisRootSize : (o.size()+i+1)*share.AxisRootSize] + } + axisRoots := &share.AxisRoots{ + RowRoots: rowRoots, + ColumnRoots: colRoots, + } + return axisRoots, nil +} + +// Close closes the file. +func (o *ODS) Close() error { + return o.fl.Close() +} + +// Sample returns share and corresponding proof for row and column indices. Implementation can +// choose which axis to use for proof. Chosen axis for proof should be indicated in the returned +// Sample. +func (o *ODS) Sample(ctx context.Context, idx shwap.SampleCoords) (shwap.Sample, error) { + // Sample proof axis is selected to optimize read performance. + // - For the first and second quadrants, we read the row axis because it is more efficient to read + // single row than reading full ODS to calculate single column + // - For the third quadrant, we read the column axis because it is more efficient to read single + // column than reading full ODS to calculate single row + // - For the fourth quadrant, it does not matter which axis we read because we need to read full ODS + // to calculate the sample + rowIdx, colIdx := idx.Row, idx.Col + + axisType, axisIdx, shrIdx := rsmt2d.Row, rowIdx, colIdx + if colIdx < o.size()/2 && rowIdx >= o.size()/2 { + axisType, axisIdx, shrIdx = rsmt2d.Col, colIdx, rowIdx + } + + axis, err := o.axis(ctx, axisType, axisIdx) + if err != nil { + return shwap.Sample{}, fmt.Errorf("reading axis: %w", err) + } + + idxNew := shwap.SampleCoords{Row: axisIdx, Col: shrIdx} + + return shwap.SampleFromShares(axis, axisType, idxNew) +} + +// AxisHalf returns half of shares axis of the given type and index. Side is determined by +// implementation. Implementations should indicate the side in the returned AxisHalf. +func (o *ODS) AxisHalf(_ context.Context, axisType rsmt2d.Axis, axisIdx int) (eds.AxisHalf, error) { + // Read the axis from the file if the axis is a row and from the top half of the square, or if the + // axis is a column and from the left half of the square. + if axisIdx < o.size()/2 { + half, err := o.readAxisHalf(axisType, axisIdx) + if err != nil { + return eds.AxisHalf{}, fmt.Errorf("reading axis half: %w", err) + } + return half, nil + } + + // if axis is from the second half of the square, read full ODS and compute the axis half + ods, err := o.readODS() + if err != nil { + return eds.AxisHalf{}, err + } + + half, err := ods.computeAxisHalf(axisType, axisIdx) + if err != nil { + return eds.AxisHalf{}, fmt.Errorf("computing axis half: %w", err) + } + return half, nil +} + +// RowNamespaceData returns data for the given namespace and row index. +func (o *ODS) RowNamespaceData( + ctx context.Context, + namespace libshare.Namespace, + rowIdx int, +) (shwap.RowNamespaceData, error) { + shares, err := o.axis(ctx, rsmt2d.Row, rowIdx) + if err != nil { + return shwap.RowNamespaceData{}, err + } + return shwap.RowNamespaceDataFromShares(shares, namespace, rowIdx) +} + +// Shares returns data shares extracted from the Accessor. +func (o *ODS) Shares(context.Context) ([]libshare.Share, error) { + ods, err := o.readODS() + if err != nil { + return nil, err + } + return ods.shares() +} + +// Reader returns binary reader for the file. It reads the shares from the ODS part of the square +// row by row. +func (o *ODS) Reader() (io.Reader, error) { + o.lock.RLock() + ods := o.ods + o.lock.RUnlock() + if ods != nil { + return ods.reader() + } + + offset := o.hdr.OffsetWithRoots() + total := int64(o.hdr.shareSize) * int64(o.size()*o.size()/4) + reader := io.NewSectionReader(o.fl, int64(offset), total) + return reader, nil +} + +func (o *ODS) axis(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) ([]libshare.Share, error) { + half, err := o.AxisHalf(ctx, axisType, axisIdx) + if err != nil { + return nil, err + } + + axis, err := half.Extended() + if err != nil { + return nil, fmt.Errorf("extending axis half: %w", err) + } + + return axis, nil +} + +func (o *ODS) readAxisHalf(axisType rsmt2d.Axis, axisIdx int) (eds.AxisHalf, error) { + o.lock.RLock() + ods := o.ods + o.lock.RUnlock() + if ods != nil { + return o.ods.axisHalf(axisType, axisIdx) + } + + axisHalf, err := readAxisHalf(o.fl, axisType, axisIdx, o.hdr, o.hdr.OffsetWithRoots()) + if err != nil { + return eds.AxisHalf{}, fmt.Errorf("reading axis half: %w", err) + } + + return eds.AxisHalf{ + Shares: axisHalf, + IsParity: false, + }, nil +} + +func (o *ODS) readODS() (square, error) { + if !o.disableCache { + o.lock.RLock() + ods := o.ods + o.lock.RUnlock() + if ods != nil { + return ods, nil + } + + // not cached, read and cache + o.lock.Lock() + defer o.lock.Unlock() + } + + offset := o.hdr.OffsetWithRoots() + shareSize := o.hdr.ShareSize() + odsBytes := o.hdr.SquareSize() / 2 + odsSizeInBytes := shareSize * odsBytes * odsBytes + reader := io.NewSectionReader(o.fl, int64(offset), int64(odsSizeInBytes)) + ods, err := readSquare(reader, shareSize, o.size()) + if err != nil { + return nil, fmt.Errorf("reading ODS: %w", err) + } + + if !o.disableCache { + o.ods = ods + } + return ods, nil +} + +func readAxisHalf(r io.ReaderAt, axisTp rsmt2d.Axis, axisIdx int, hdr *headerV0, offset int) ([]libshare.Share, error) { + switch axisTp { + case rsmt2d.Row: + return readRowHalf(r, axisIdx, hdr, offset) + case rsmt2d.Col: + return readColHalf(r, axisIdx, hdr, offset) + default: + return nil, fmt.Errorf("unknown axis") + } +} + +// readRowHalf reads specific Row half from the file in a single IO operation. +// If some or all shares are missing, tail padding shares are returned instead. +func readRowHalf(r io.ReaderAt, rowIdx int, hdr *headerV0, offset int) ([]libshare.Share, error) { + odsLn := hdr.SquareSize() / 2 + rowOffset := rowIdx * odsLn * hdr.ShareSize() + offset += rowOffset + + shares := make([]libshare.Share, odsLn) + axsData := make([]byte, odsLn*hdr.ShareSize()) + n, err := r.ReadAt(axsData, int64(offset)) + if err != nil && !errors.Is(err, io.EOF) { + // unknown error + return nil, err + } + + shrsRead := n / hdr.ShareSize() + for i := range shares { + if i > shrsRead-1 { + // partial or empty row was read + // fill the rest with tail padding it + shares[i] = libshare.TailPaddingShare() + continue + } + sh, err := libshare.NewShare(axsData[i*hdr.ShareSize() : (i+1)*hdr.ShareSize()]) + if err != nil { + return nil, err + } + shares[i] = *sh + } + return shares, nil +} + +// readColHalf reads specific Col half from the file in a single IO operation. +// If some or all shares are missing, tail padding shares are returned instead. +func readColHalf(r io.ReaderAt, colIdx int, hdr *headerV0, offset int) ([]libshare.Share, error) { + odsLn := hdr.SquareSize() / 2 + shares := make([]libshare.Share, odsLn) + for i := range shares { + pos := colIdx + i*odsLn + offset := offset + pos*hdr.ShareSize() + + shr := make([]byte, hdr.ShareSize()) + n, err := r.ReadAt(shr, int64(offset)) + if err != nil && !errors.Is(err, io.EOF) { + // unknown error + return nil, err + } + if n == 0 { + // no shares left + // fill the rest with tail padding + for ; i < len(shares); i++ { + shares[i] = libshare.TailPaddingShare() + } + return shares, nil + } + + sh, err := libshare.NewShare(shr) + if err != nil { + return nil, err + } + // we got a share + shares[i] = *sh + } + return shares, nil +} + +// filledSharesAmount returns the amount of shares in the ODS that are not tail padding. +func filledSharesAmount(eds *rsmt2d.ExtendedDataSquare) (int, error) { + var amount int + for i := range eds.Width() / 2 { + for j := range eds.Width() / 2 { + rawShr := eds.GetCell(i, j) + shr, err := libshare.NewShare(rawShr) + if err != nil { + return 0, fmt.Errorf("creating share at(%d,%d): %w", i, j, err) + } + if shr.Namespace().Equals(libshare.TailPaddingNamespace) { + break + } + amount++ + } + } + return amount, nil +} diff --git a/store/file/ods_q4.go b/store/file/ods_q4.go new file mode 100644 index 0000000000..f0ca686094 --- /dev/null +++ b/store/file/ods_q4.go @@ -0,0 +1,191 @@ +package file + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "sync" + "sync/atomic" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ eds.AccessorStreamer = (*ODSQ4)(nil) + +// ODSQ4 is an Accessor that combines ODS and Q4 files. +// It extends the ODS with the ability to read Q4 of the EDS. +// Reading from the fourth quadrant allows to efficiently read samples from Q2 and Q4 quadrants of +// the square, as well as reading columns from Q3 and Q4 quadrants. Reading from Q4 in those cases +// is more efficient than reading from Q1, because it would require reading the whole Q1 quadrant +// and reconstructing the data from it. It opens Q4 file lazily on the first read attempt. +type ODSQ4 struct { + ods *ODS + + pathQ4 string + q4Mu sync.Mutex + q4OpenAttempted atomic.Bool + q4 *q4 +} + +// CreateODSQ4 creates ODS and Q4 files under the given FS paths. +func CreateODSQ4( + pathODS, pathQ4 string, + roots *share.AxisRoots, + eds *rsmt2d.ExtendedDataSquare, +) error { + errCh := make(chan error) + go func() { + // doing this async shaves off ~27% of time for 128 ODS + // for bigger ODSes the discrepancy is even bigger + errCh <- createQ4(pathQ4, eds) + }() + + err := CreateODS(pathODS, roots, eds) + q4Err := <-errCh + + if err != nil && q4Err != nil { + return fmt.Errorf("creating ODS and Q4 files: %w", errors.Join(err, q4Err)) + } + if err != nil { + return fmt.Errorf("creating ODS file: %w", err) + } + if q4Err != nil { + return fmt.Errorf("creating Q4 file: %w", q4Err) + } + return nil +} + +// ValidateODSQ4Size checks the size of the ODS and Q4 files under the given FS paths. +func ValidateODSQ4Size(pathODS, pathQ4 string, eds *rsmt2d.ExtendedDataSquare) error { + err := ValidateODSSize(pathODS, eds) + if err != nil { + return fmt.Errorf("validating ODS file size: %w", err) + } + err = validateQ4Size(pathQ4, eds) + if err != nil { + return fmt.Errorf("validating Q4 file size: %w", err) + } + return nil +} + +// ODSWithQ4 returns ODSQ4 instance over ODS. It opens Q4 file lazily under the given path. +func ODSWithQ4(ods *ODS, pathQ4 string) *ODSQ4 { + return &ODSQ4{ + ods: ods, + pathQ4: pathQ4, + } +} + +func (odsq4 *ODSQ4) tryLoadQ4() *q4 { + // If Q4 was attempted to be opened before, return. + if odsq4.q4OpenAttempted.Load() { + return odsq4.q4 + } + + odsq4.q4Mu.Lock() + defer odsq4.q4Mu.Unlock() + if odsq4.q4OpenAttempted.Load() { + return odsq4.q4 + } + + q4, err := openQ4(odsq4.pathQ4, odsq4.ods.hdr) + // store q4 opened bool before updating atomic value to allow next read attempts to use it + odsq4.q4 = q4 + // even if error occurred, store q4 opened bool to avoid trying to open it again + odsq4.q4OpenAttempted.Store(true) + if errors.Is(err, os.ErrNotExist) { + return nil + } + if err != nil { + log.Errorf("opening Q4 file %s: %s", odsq4.pathQ4, err) + return nil + } + return q4 +} + +func (odsq4 *ODSQ4) Size(ctx context.Context) int { + return odsq4.ods.Size(ctx) +} + +func (odsq4 *ODSQ4) DataHash(ctx context.Context) (share.DataHash, error) { + return odsq4.ods.DataHash(ctx) +} + +func (odsq4 *ODSQ4) AxisRoots(ctx context.Context) (*share.AxisRoots, error) { + return odsq4.ods.AxisRoots(ctx) +} + +func (odsq4 *ODSQ4) Sample(ctx context.Context, idx shwap.SampleCoords) (shwap.Sample, error) { + // use native AxisHalf implementation, to read axis from q4 quadrant when possible + half, err := odsq4.AxisHalf(ctx, rsmt2d.Row, idx.Row) + if err != nil { + return shwap.Sample{}, fmt.Errorf("reading axis: %w", err) + } + shares, err := half.Extended() + if err != nil { + return shwap.Sample{}, fmt.Errorf("extending shares: %w", err) + } + + return shwap.SampleFromShares(shares, rsmt2d.Row, idx) +} + +func (odsq4 *ODSQ4) AxisHalf(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) (eds.AxisHalf, error) { + size := odsq4.Size(ctx) // TODO(@Wondertan): Should return error. + + if axisIdx >= size/2 { + // lazy load Q4 file and read axis from it if loaded + if q4 := odsq4.tryLoadQ4(); q4 != nil { + return q4.axisHalf(axisType, axisIdx) + } + } + + return odsq4.ods.AxisHalf(ctx, axisType, axisIdx) +} + +func (odsq4 *ODSQ4) RowNamespaceData(ctx context.Context, + namespace libshare.Namespace, + rowIdx int, +) (shwap.RowNamespaceData, error) { + half, err := odsq4.AxisHalf(ctx, rsmt2d.Row, rowIdx) + if err != nil { + return shwap.RowNamespaceData{}, fmt.Errorf("reading axis: %w", err) + } + shares, err := half.Extended() + if err != nil { + return shwap.RowNamespaceData{}, fmt.Errorf("extending shares: %w", err) + } + return shwap.RowNamespaceDataFromShares(shares, namespace, rowIdx) +} + +func (odsq4 *ODSQ4) Shares(ctx context.Context) ([]libshare.Share, error) { + return odsq4.ods.Shares(ctx) +} + +func (odsq4 *ODSQ4) Reader() (io.Reader, error) { + return odsq4.ods.Reader() +} + +func (odsq4 *ODSQ4) Close() error { + err := odsq4.ods.Close() + if err != nil { + err = fmt.Errorf("closing ODS file: %w", err) + } + + odsq4.q4Mu.Lock() // wait in case file is being opened + defer odsq4.q4Mu.Unlock() + if odsq4.q4 != nil { + errQ4 := odsq4.q4.close() + if errQ4 != nil { + errQ4 = fmt.Errorf("closing Q4 file: %w", errQ4) + err = errors.Join(err, errQ4) + } + } + return err +} diff --git a/store/file/ods_q4_test.go b/store/file/ods_q4_test.go new file mode 100644 index 0000000000..6e37e843b4 --- /dev/null +++ b/store/file/ods_q4_test.go @@ -0,0 +1,193 @@ +package file + +import ( + "context" + "fmt" + "io" + "os" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/rand" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/eds/edstest" +) + +func TestCreateODSQ4File(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + + edsIn := edstest.RandEDS(t, 8) + odsq4 := createODSQ4File(t, edsIn) + + shares, err := odsq4.Shares(ctx) + require.NoError(t, err) + expected := edsIn.FlattenedODS() + require.Equal(t, expected, libshare.ToBytes(shares)) + require.NoError(t, odsq4.Close()) +} + +func TestODSQ4File(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + t.Cleanup(cancel) + + ODSSize := 16 + eds.TestSuiteAccessor(ctx, t, createODSQ4Accessor, ODSSize) + eds.TestStreamer(ctx, t, createODSQ4AccessorStreamer, ODSSize) +} + +func TestValidateODSQ4FileSize(t *testing.T) { + edses := []struct { + name string + eds *rsmt2d.ExtendedDataSquare + }{ + { + name: "no padding", + eds: edstest.RandEDS(t, 8), + }, + { + name: "with padding", + eds: edstest.RandEDSWithTailPadding(t, 8, 11), + }, + { + name: "empty", eds: share.EmptyEDS(), + }, + } + + tests := []struct { + name string + createFile func(pathODS, pathQ4 string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDataSquare) error + valid bool + }{ + { + name: "valid", + createFile: CreateODSQ4, + valid: true, + }, + { + name: "shorter q4", + createFile: func(pathODS, pathQ4 string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDataSquare) error { + err := CreateODSQ4(pathODS, pathQ4, roots, eds) + if err != nil { + return err + } + file, err := os.OpenFile(pathQ4, os.O_RDWR, 0) + if err != nil { + return err + } + defer file.Close() + info, err := file.Stat() + if err != nil { + return err + } + return file.Truncate(info.Size() - 1) + }, + valid: false, + }, + { + name: "longer q4", + createFile: func(pathODS, pathQ4 string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDataSquare) error { + err := CreateODSQ4(pathODS, pathQ4, roots, eds) + if err != nil { + return err + } + file, err := os.OpenFile(pathQ4, os.O_RDWR, 0) + if err != nil { + return err + } + defer file.Close() + // append 1 byte to the file + _, err = file.Seek(0, io.SeekEnd) + if err != nil { + return err + } + _, err = file.Write([]byte{0}) + return err + }, + valid: false, + }, + } + + for _, tt := range tests { + for _, eds := range edses { + t.Run(fmt.Sprintf("%s/%s", tt.name, eds.name), func(t *testing.T) { + pathODS := t.TempDir() + tt.name + eds.name + pathQ4 := pathODS + ".q4" + roots, err := share.NewAxisRoots(eds.eds) + require.NoError(t, err) + err = tt.createFile(pathODS, pathQ4, roots, eds.eds) + require.NoError(t, err) + + err = ValidateODSQ4Size(pathODS, pathQ4, eds.eds) + require.Equal(t, tt.valid, err == nil) + }) + } + } +} + +// BenchmarkAxisFromODSQ4File/Size:32/ProofType:row/squareHalf:0-16 354836 3345 ns/op +// BenchmarkAxisFromODSQ4File/Size:32/ProofType:row/squareHalf:1-16 339547 3187 ns/op +// BenchmarkAxisFromODSQ4File/Size:32/ProofType:col/squareHalf:0-16 69364 16440 ns/op +// BenchmarkAxisFromODSQ4File/Size:32/ProofType:col/squareHalf:1-16 66928 15964 ns/op +// BenchmarkAxisFromODSQ4File/Size:64/ProofType:row/squareHalf:0-16 223290 5184 ns/op +// BenchmarkAxisFromODSQ4File/Size:64/ProofType:row/squareHalf:1-16 194018 5240 ns/op +// BenchmarkAxisFromODSQ4File/Size:64/ProofType:col/squareHalf:0-16 39949 29549 ns/op +// BenchmarkAxisFromODSQ4File/Size:64/ProofType:col/squareHalf:1-16 39356 29912 ns/op +// BenchmarkAxisFromODSQ4File/Size:128/ProofType:row/squareHalf:0-16 134220 8903 ns/op +// BenchmarkAxisFromODSQ4File/Size:128/ProofType:row/squareHalf:1-16 125110 8789 ns/op +// BenchmarkAxisFromODSQ4File/Size:128/ProofType:col/squareHalf:0-16 15075 74996 ns/op +// BenchmarkAxisFromODSQ4File/Size:128/ProofType:col/squareHalf:1-16 15530 74855 ns/op +func BenchmarkAxisFromODSQ4File(b *testing.B) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + b.Cleanup(cancel) + + minSize, maxSize := 32, 128 + eds.BenchGetHalfAxisFromAccessor(ctx, b, createODSQ4Accessor, minSize, maxSize) +} + +// BenchmarkSampleFromODSQ4File/Size:32/quadrant:1-16 14260 82827 ns/op +// BenchmarkSampleFromODSQ4File/Size:32/quadrant:2-16 14281 85465 ns/op +// BenchmarkSampleFromODSQ4File/Size:32/quadrant:3-16 12938 91213 ns/op +// BenchmarkSampleFromODSQ4File/Size:32/quadrant:4-16 12934 94077 ns/op +// BenchmarkSampleFromODSQ4File/Size:64/quadrant:1-16 7497 172978 ns/op +// BenchmarkSampleFromODSQ4File/Size:64/quadrant:2-16 6332 191139 ns/op +// BenchmarkSampleFromODSQ4File/Size:64/quadrant:3-16 5852 214140 ns/op +// BenchmarkSampleFromODSQ4File/Size:64/quadrant:4-16 5899 215875 ns/op +// BenchmarkSampleFromODSQ4File/Size:128/quadrant:1-16 3520 399728 ns/op +// BenchmarkSampleFromODSQ4File/Size:128/quadrant:2-16 3242 410557 ns/op +// BenchmarkSampleFromODSQ4File/Size:128/quadrant:3-16 2590 424491 ns/op +// BenchmarkSampleFromODSQ4File/Size:128/quadrant:4-16 2812 444697 ns/op +func BenchmarkSampleFromODSQ4File(b *testing.B) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + b.Cleanup(cancel) + + minSize, maxSize := 32, 128 + eds.BenchGetSampleFromAccessor(ctx, b, createODSQ4Accessor, minSize, maxSize) +} + +func createODSQ4AccessorStreamer(t testing.TB, eds *rsmt2d.ExtendedDataSquare) eds.AccessorStreamer { + return createODSQ4File(t, eds) +} + +func createODSQ4Accessor(t testing.TB, eds *rsmt2d.ExtendedDataSquare) eds.Accessor { + return createODSQ4File(t, eds) +} + +func createODSQ4File(t testing.TB, eds *rsmt2d.ExtendedDataSquare) *ODSQ4 { + path := t.TempDir() + "/" + strconv.Itoa(rand.Intn(1000)) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + pathODS, pathQ4 := path+".ods", path+".q4" + err = CreateODSQ4(pathODS, pathQ4, roots, eds) + require.NoError(t, err) + ods, err := OpenODS(pathODS) + require.NoError(t, err) + return ODSWithQ4(ods, pathQ4) +} diff --git a/store/file/ods_test.go b/store/file/ods_test.go new file mode 100644 index 0000000000..fe4c7cd456 --- /dev/null +++ b/store/file/ods_test.go @@ -0,0 +1,267 @@ +package file + +import ( + "context" + "fmt" + "io" + "os" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/rand" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/eds/edstest" +) + +func TestCreateODSFile(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + + edsIn := edstest.RandEDS(t, 8) + f := createODSFile(t, edsIn) + readRoots, err := share.NewAxisRoots(edsIn) + require.NoError(t, err) + + shares, err := f.Shares(ctx) + require.NoError(t, err) + + expected := edsIn.FlattenedODS() + require.Equal(t, expected, libshare.ToBytes(shares)) + + roots, err := f.AxisRoots(ctx) + require.NoError(t, err) + require.Equal(t, share.DataHash(roots.Hash()), f.hdr.datahash) + require.True(t, roots.Equals(readRoots)) + require.NoError(t, f.Close()) +} + +func TestReadODSFromFile(t *testing.T) { + eds := edstest.RandEDS(t, 8) + f := createODSFile(t, eds) + + ods, err := f.readODS() + require.NoError(t, err) + for i, row := range ods { + original := eds.Row(uint(i))[:eds.Width()/2] + require.True(t, len(original) == len(row)) + require.Equal(t, original, libshare.ToBytes(row)) + } +} + +func TestODSFile(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + t.Cleanup(cancel) + + ODSSize := 16 + eds.TestSuiteAccessor(ctx, t, createODSAccessor, ODSSize) + eds.TestStreamer(ctx, t, createCachedStreamer, ODSSize) + eds.TestStreamer(ctx, t, createODSAccessorStreamer, ODSSize) +} + +func TestValidateODSSize(t *testing.T) { + edses := []struct { + name string + eds *rsmt2d.ExtendedDataSquare + }{ + { + name: "no padding", + eds: edstest.RandEDS(t, 8), + }, + { + name: "with padding", + eds: edstest.RandEDSWithTailPadding(t, 8, 11), + }, + { + name: "empty", eds: share.EmptyEDS(), + }, + } + + tests := []struct { + name string + createFile func(path string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDataSquare) error + valid bool + }{ + { + name: "valid", + createFile: CreateODS, + valid: true, + }, + { + name: "shorter", + createFile: func(path string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDataSquare) error { + err := CreateODS(path, roots, eds) + if err != nil { + return err + } + file, err := os.OpenFile(path, os.O_RDWR, 0) + if err != nil { + return err + } + defer file.Close() + info, err := file.Stat() + if err != nil { + return err + } + return file.Truncate(info.Size() - 1) + }, + valid: false, + }, + { + name: "longer", + createFile: func(path string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDataSquare) error { + err := CreateODS(path, roots, eds) + if err != nil { + return err + } + file, err := os.OpenFile(path, os.O_RDWR, 0) + if err != nil { + return err + } + defer file.Close() + // append 1 byte to the file + _, err = file.Seek(0, io.SeekEnd) + if err != nil { + return err + } + _, err = file.Write([]byte{0}) + return err + }, + valid: false, + }, + } + + for _, tt := range tests { + for _, eds := range edses { + t.Run(fmt.Sprintf("%s/%s", tt.name, eds.name), func(t *testing.T) { + path := t.TempDir() + tt.name + eds.name + roots, err := share.NewAxisRoots(eds.eds) + require.NoError(t, err) + err = tt.createFile(path, roots, eds.eds) + require.NoError(t, err) + + err = ValidateODSSize(path, eds.eds) + require.Equal(t, tt.valid, err == nil) + }) + } + } +} + +// BenchmarkAxisFromODSFile/Size:32/ProofType:row/squareHalf:0-16 382011 3104 ns/op +// BenchmarkAxisFromODSFile/Size:32/ProofType:row/squareHalf:1-16 9320 122408 ns/op +// BenchmarkAxisFromODSFile/Size:32/ProofType:col/squareHalf:0-16 4408911 266.5 ns/op +// BenchmarkAxisFromODSFile/Size:32/ProofType:col/squareHalf:1-16 9488 119472 ns/op +// BenchmarkAxisFromODSFile/Size:64/ProofType:row/squareHalf:0-16 240913 5239 ns/op +// BenchmarkAxisFromODSFile/Size:64/ProofType:row/squareHalf:1-16 1018 1249622 ns/op +// BenchmarkAxisFromODSFile/Size:64/ProofType:col/squareHalf:0-16 2614063 451.8 ns/op +// BenchmarkAxisFromODSFile/Size:64/ProofType:col/squareHalf:1-16 1917 661510 ns/op +// BenchmarkAxisFromODSFile/Size:128/ProofType:row/squareHalf:0-16 119324 10425 ns/op +// BenchmarkAxisFromODSFile/Size:128/ProofType:row/squareHalf:1-16 163 9926752 ns/op +// BenchmarkAxisFromODSFile/Size:128/ProofType:col/squareHalf:0-16 1634124 726.2 ns/op +// BenchmarkAxisFromODSFile/Size:128/ProofType:col/squareHalf:1-16 205 5508394 ns/op +func BenchmarkAxisFromODSFile(b *testing.B) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + b.Cleanup(cancel) + + minSize, maxSize := 32, 128 + eds.BenchGetHalfAxisFromAccessor(ctx, b, createODSAccessor, minSize, maxSize) +} + +// BenchmarkAxisFromODSFileDisabledCache/Size:32/ProofType:row/squareHalf:0-16 378975 3141 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:32/ProofType:row/squareHalf:1-16 1026 1175651 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:32/ProofType:col/squareHalf:0-16 80200 14721 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:32/ProofType:col/squareHalf:1-16 1014 1180527 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:64/ProofType:row/squareHalf:0-16 212041 5417 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:64/ProofType:row/squareHalf:1-16 253 4205953 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:64/ProofType:col/squareHalf:0-16 35289 34033 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:64/ProofType:col/squareHalf:1-16 325 3229517 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:128/ProofType:row/squareHalf:0-16 132261 8535 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:128/ProofType:row/squareHalf:1-16 48 22963229 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:128/ProofType:col/squareHalf:0-16 19053 62858 ns/op +// BenchmarkAxisFromODSFileDisabledCache/Size:128/ProofType:col/squareHalf:1-16 48 21185201 ns/op +func BenchmarkAxisFromODSFileDisabledCache(b *testing.B) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + b.Cleanup(cancel) + + minSize, maxSize := 32, 128 + eds.BenchGetHalfAxisFromAccessor(ctx, b, createODSFileDisabledCache, minSize, maxSize) +} + +// BenchmarkSampleFromODSFile/Size:32/quadrant:1-16 13684 87558 ns/op +// BenchmarkSampleFromODSFile/Size:32/quadrant:2-16 13358 85677 ns/op +// BenchmarkSampleFromODSFile/Size:32/quadrant:3-16 10000 102631 ns/op +// BenchmarkSampleFromODSFile/Size:32/quadrant:4-16 5175 222615 ns/op +// BenchmarkSampleFromODSFile/Size:64/quadrant:1-16 7142 173784 ns/op +// BenchmarkSampleFromODSFile/Size:64/quadrant:2-16 6820 171602 ns/op +// BenchmarkSampleFromODSFile/Size:64/quadrant:3-16 5232 201875 ns/op +// BenchmarkSampleFromODSFile/Size:64/quadrant:4-16 1448 1035275 ns/op +// BenchmarkSampleFromODSFile/Size:128/quadrant:1-16 3829 359528 ns/op +// BenchmarkSampleFromODSFile/Size:128/quadrant:2-16 3303 358142 ns/op +// BenchmarkSampleFromODSFile/Size:128/quadrant:3-16 2666 431895 ns/op +// BenchmarkSampleFromODSFile/Size:128/quadrant:4-16 183 7347936 ns/op +func BenchmarkSampleFromODSFile(b *testing.B) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + b.Cleanup(cancel) + + minSize, maxSize := 32, 128 + eds.BenchGetSampleFromAccessor(ctx, b, createODSAccessor, minSize, maxSize) +} + +// BenchmarkSampleFromODSFileDisabledCache/Size:32/quadrant:1 +// BenchmarkSampleFromODSFileDisabledCache/Size:32/quadrant:1-16 13152 85301 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:32/quadrant:2-16 14140 84876 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:32/quadrant:3-16 11756 102360 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:32/quadrant:4-16 985 1292232 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:64/quadrant:1-16 7678 172306 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:64/quadrant:2-16 5744 176533 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:64/quadrant:3-16 6022 207884 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:64/quadrant:4-16 304 3881858 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:128/quadrant:1-16 3697 355835 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:128/quadrant:2-16 3558 360162 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:128/quadrant:3-16 3027 410976 ns/op +// BenchmarkSampleFromODSFileDisabledCache/Size:128/quadrant:4-16 54 21796460 ns/op +func BenchmarkSampleFromODSFileDisabledCache(b *testing.B) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + b.Cleanup(cancel) + + minSize, maxSize := 32, 128 + eds.BenchGetSampleFromAccessor(ctx, b, createODSFileDisabledCache, minSize, maxSize) +} + +func createODSAccessorStreamer(t testing.TB, eds *rsmt2d.ExtendedDataSquare) eds.AccessorStreamer { + return createODSFile(t, eds) +} + +func createODSAccessor(t testing.TB, eds *rsmt2d.ExtendedDataSquare) eds.Accessor { + return createODSFile(t, eds) +} + +func createCachedStreamer(t testing.TB, eds *rsmt2d.ExtendedDataSquare) eds.AccessorStreamer { + f := createODSFile(t, eds) + _, err := f.readODS() + require.NoError(t, err) + return f +} + +func createODSFile(t testing.TB, eds *rsmt2d.ExtendedDataSquare) *ODS { + path := t.TempDir() + "/" + strconv.Itoa(rand.Intn(1000)) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + err = CreateODS(path, roots, eds) + require.NoError(t, err) + ods, err := OpenODS(path) + require.NoError(t, err) + return ods +} + +func createODSFileDisabledCache(t testing.TB, eds *rsmt2d.ExtendedDataSquare) eds.Accessor { + ods := createODSFile(t, eds) + ods.disableCache = true + return ods +} diff --git a/store/file/q4.go b/store/file/q4.go new file mode 100644 index 0000000000..7025f8adb3 --- /dev/null +++ b/store/file/q4.go @@ -0,0 +1,128 @@ +package file + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share/eds" +) + +// q4 stores the fourth quadrant of the square. +type q4 struct { + hdr *headerV0 + file *os.File +} + +// createQ4 creates a new file under given FS path and +// writes the Q4 into it out of given EDS. +// It may leave partially written file if any of the writes fail. +func createQ4( + path string, + eds *rsmt2d.ExtendedDataSquare, +) error { + mod := os.O_RDWR | os.O_CREATE | os.O_EXCL // ensure we fail if already exist + f, err := os.OpenFile(path, mod, filePermissions) + if err != nil { + return fmt.Errorf("creating Q4 file: %w", err) + } + + err = writeQ4File(f, eds) + if errClose := f.Close(); errClose != nil { + err = errors.Join(err, fmt.Errorf("closing created Q4 file: %w", errClose)) + } + + return err +} + +// writeQ4File full Q4 content into OS File. +func writeQ4File(f *os.File, eds *rsmt2d.ExtendedDataSquare) error { + // buffering gives us ~4x speed up + buf := bufio.NewWriterSize(f, writeBufferSize) + + if err := writeQ4(buf, eds); err != nil { + return fmt.Errorf("writing Q4: %w", err) + } + + if err := buf.Flush(); err != nil { + return fmt.Errorf("flushing Q4: %w", err) + } + + return nil +} + +// writeQ4 writes the forth quadrant of the square to the writer. It writes the quadrant in +// row-major order. +func writeQ4(w io.Writer, eds *rsmt2d.ExtendedDataSquare) error { + half := eds.Width() / 2 + for i := range half { + for j := range half { + shr := eds.GetCell(i+half, j+half) // TODO: Avoid copying inside GetCell + _, err := w.Write(shr) + if err != nil { + return fmt.Errorf("writing share: %w", err) + } + } + } + return nil +} + +func validateQ4Size(path string, eds *rsmt2d.ExtendedDataSquare) error { + f, err := os.Open(path) + if err != nil { + return fmt.Errorf("opening file: %w", err) + } + defer f.Close() + + odsSize := int(eds.Width() / 2) + shareSize := len(eds.GetCell(0, 0)) + expectedSize := shareSize * odsSize * odsSize + + info, err := f.Stat() + if err != nil { + return fmt.Errorf("getting file info: %w", err) + } + if info.Size() != int64(expectedSize) { + return fmt.Errorf("file size mismatch: expected %d, got %d", expectedSize, info.Size()) + } + return nil +} + +// openQ4 opens an existing Q4 file under given FS path. +func openQ4(path string, hdr *headerV0) (*q4, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + + return &q4{ + hdr: hdr, + file: f, + }, nil +} + +func (q4 *q4) close() error { + return q4.file.Close() +} + +func (q4 *q4) axisHalf(axisType rsmt2d.Axis, axisIdx int) (eds.AxisHalf, error) { + size := q4.hdr.SquareSize() + q4AxisIdx := axisIdx - size/2 + if q4AxisIdx < 0 { + return eds.AxisHalf{}, fmt.Errorf("invalid axis index for Q4: %d", axisIdx) + } + + axisHalf, err := readAxisHalf(q4.file, axisType, q4AxisIdx, q4.hdr, 0) + if err != nil { + return eds.AxisHalf{}, fmt.Errorf("reading axis half from Q4: %w", err) + } + + return eds.AxisHalf{ + Shares: axisHalf, + IsParity: true, + }, nil +} diff --git a/store/file/square.go b/store/file/square.go new file mode 100644 index 0000000000..fc6bdbbaf9 --- /dev/null +++ b/store/file/square.go @@ -0,0 +1,143 @@ +package file + +import ( + "fmt" + "io" + + "golang.org/x/sync/errgroup" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share/eds" +) + +type square [][]libshare.Share + +// readSquare reads Shares from the reader and returns a square. It assumes that the reader is +// positioned at the beginning of the Shares. It knows the size of the Shares and the size of the +// square, so reads from reader are limited to exactly the amount of data required. +func readSquare(r io.Reader, shareSize, edsSize int) (square, error) { + odsLn := edsSize / 2 + + shares, err := eds.ReadShares(r, shareSize, odsLn) + if err != nil { + return nil, fmt.Errorf("reading shares: %w", err) + } + square := make(square, odsLn) + for i := range square { + square[i] = shares[i*odsLn : (i+1)*odsLn] + } + return square, nil +} + +func (s square) reader() (io.Reader, error) { + if s == nil { + return nil, fmt.Errorf("ods file not cached") + } + getShare := func(rowIdx, colIdx int) (libshare.Share, error) { + return s[rowIdx][colIdx], nil + } + reader := eds.NewShareReader(s.size(), getShare) + return reader, nil +} + +func (s square) size() int { + return len(s) +} + +func (s square) shares() ([]libshare.Share, error) { + shares := make([]libshare.Share, 0, s.size()*s.size()) + for _, row := range s { + shares = append(shares, row...) + } + return shares, nil +} + +func (s square) axisHalf(axisType rsmt2d.Axis, axisIdx int) (eds.AxisHalf, error) { + if s == nil { + return eds.AxisHalf{}, fmt.Errorf("square is nil") + } + + if axisIdx >= s.size() { + return eds.AxisHalf{}, fmt.Errorf("index is out of square bounds") + } + + // square stores rows directly in high level slice, so we can return by accessing row by index + if axisType == rsmt2d.Row { + row := s[axisIdx] + return eds.AxisHalf{ + Shares: row, + IsParity: false, + }, nil + } + + // construct half column from row ordered square + col := make([]libshare.Share, s.size()) + for i := 0; i < s.size(); i++ { + col[i] = s[i][axisIdx] + } + return eds.AxisHalf{ + Shares: col, + IsParity: false, + }, nil +} + +func (s square) computeAxisHalf( + axisType rsmt2d.Axis, + axisIdx int, +) (eds.AxisHalf, error) { + shares := make([]libshare.Share, s.size()) + + // extend opposite half of the square while collecting Shares for the first half of required axis + g := errgroup.Group{} + opposite := oppositeAxis(axisType) + for i := 0; i < s.size(); i++ { + g.Go(func() error { + half, err := s.axisHalf(opposite, i) + if err != nil { + return err + } + + enc, err := codec.Encoder(s.size() * 2) + if err != nil { + return fmt.Errorf("getting encoder: %w", err) + } + + shards := make([][]byte, s.size()*2) + if half.IsParity { + copy(shards[s.size():], libshare.ToBytes(half.Shares)) + } else { + copy(shards, libshare.ToBytes(half.Shares)) + } + + target := make([]bool, s.size()*2) + target[axisIdx] = true + + err = enc.ReconstructSome(shards, target) + if err != nil { + return fmt.Errorf("reconstruct some: %w", err) + } + + shard, err := libshare.NewShare(shards[axisIdx]) + if err != nil { + return fmt.Errorf("creating share: %w", err) + } + shares[i] = *shard + return nil + }) + } + + err := g.Wait() + return eds.AxisHalf{ + Shares: shares, + IsParity: false, + }, err +} + +func oppositeAxis(axis rsmt2d.Axis) rsmt2d.Axis { + if axis == rsmt2d.Col { + return rsmt2d.Row + } + return rsmt2d.Col +} diff --git a/store/getter.go b/store/getter.go new file mode 100644 index 0000000000..7a94c408ac --- /dev/null +++ b/store/getter.go @@ -0,0 +1,112 @@ +package store + +import ( + "context" + "errors" + "fmt" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/header" + "github.com/celestiaorg/celestia-node/libs/utils" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +var _ shwap.Getter = (*Getter)(nil) + +type Getter struct { + store *Store +} + +func NewGetter(store *Store) *Getter { + return &Getter{store: store} +} + +func (g *Getter) GetSamples(ctx context.Context, hdr *header.ExtendedHeader, + indices []shwap.SampleCoords, +) ([]shwap.Sample, error) { + acc, err := g.store.GetByHeight(ctx, hdr.Height()) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, shwap.ErrNotFound + } + return nil, fmt.Errorf("get accessor from store:%w", err) + } + defer utils.CloseAndLog(log.With("height", hdr.Height()), "getter/sample", acc) + + smpls := make([]shwap.Sample, len(indices)) + for i, idx := range indices { + smpl, err := acc.Sample(ctx, idx) + if err != nil { + return nil, fmt.Errorf("get sample from accessor:%w", err) + } + + smpls[i] = smpl + } + + return smpls, nil +} + +func (g *Getter) GetEDS(ctx context.Context, h *header.ExtendedHeader) (*rsmt2d.ExtendedDataSquare, error) { + acc, err := g.store.GetByHeight(ctx, h.Height()) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, shwap.ErrNotFound + } + return nil, fmt.Errorf("get accessor from store:%w", err) + } + logger := log.With("height", h.Height()) + defer utils.CloseAndLog(logger, "getter/eds", acc) + + shares, err := acc.Shares(ctx) + if err != nil { + return nil, fmt.Errorf("get shares from accessor:%w", err) + } + rsmt2d, err := eds.Rsmt2DFromShares(shares, len(h.DAH.RowRoots)/2) + if err != nil { + return nil, fmt.Errorf("build eds from shares:%w", err) + } + return rsmt2d.ExtendedDataSquare, nil +} + +func (g *Getter) GetRow(ctx context.Context, h *header.ExtendedHeader, rowIdx int) (shwap.Row, error) { + acc, err := g.store.GetByHeight(ctx, h.Height()) + if err != nil { + if errors.Is(err, ErrNotFound) { + return shwap.Row{}, shwap.ErrNotFound + } + return shwap.Row{}, fmt.Errorf("getting accessor from store: %w", err) + } + axisHalf, err := acc.AxisHalf(ctx, rsmt2d.Row, rowIdx) + if err != nil { + return shwap.Row{}, fmt.Errorf("getting axis half from accessor: %w", err) + } + return axisHalf.ToRow(), nil +} + +func (g *Getter) GetNamespaceData( + ctx context.Context, + h *header.ExtendedHeader, + ns libshare.Namespace, +) (shwap.NamespaceData, error) { + acc, err := g.store.GetByHeight(ctx, h.Height()) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, shwap.ErrNotFound + } + return nil, fmt.Errorf("get accessor from store:%w", err) + } + logger := log.With( + "height", h.Height(), + "namespace", ns.String(), + ) + defer utils.CloseAndLog(logger, "getter/nd", acc) + + nd, err := eds.NamespaceData(ctx, acc, ns) + if err != nil { + return nil, fmt.Errorf("get nd from accessor:%w", err) + } + return nd, nil +} diff --git a/store/getter_test.go b/store/getter_test.go new file mode 100644 index 0000000000..0042f43ecf --- /dev/null +++ b/store/getter_test.go @@ -0,0 +1,116 @@ +package store + +import ( + "context" + "sync/atomic" + "testing" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + + "github.com/celestiaorg/celestia-node/header/headertest" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/share/shwap" +) + +func TestStoreGetter(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + edsStore, err := NewStore(DefaultParameters(), t.TempDir()) + require.NoError(t, err) + + sg := NewGetter(edsStore) + height := atomic.Uint64{} + + t.Run("GetShare", func(t *testing.T) { + eds, roots := randomEDS(t) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + height := height.Add(1) + eh.RawHeader.Height = int64(height) + + err := edsStore.PutODSQ4(ctx, eh.DAH, height, eds) + require.NoError(t, err) + + squareSize := int(eds.Width()) + for i := 0; i < squareSize; i++ { + for j := 0; j < squareSize; j++ { + idx := shwap.SampleCoords{Row: i, Col: j} + + smpls, err := sg.GetSamples(ctx, eh, []shwap.SampleCoords{idx}) + require.NoError(t, err) + require.Equal(t, eds.GetCell(uint(i), uint(j)), smpls[0].Share.ToBytes()) + } + } + + // doesn't panic on indexes too high + bigIdx := squareSize * squareSize + _, err = sg.GetSamples(ctx, eh, []shwap.SampleCoords{{Row: bigIdx, Col: bigIdx}}) + require.ErrorIs(t, err, shwap.ErrOutOfBounds) + }) + + t.Run("GetEDS", func(t *testing.T) { + eds, roots := randomEDS(t) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + height := height.Add(1) + eh.RawHeader.Height = int64(height) + + err := edsStore.PutODSQ4(ctx, eh.DAH, height, eds) + require.NoError(t, err) + + retrievedEDS, err := sg.GetEDS(ctx, eh) + require.NoError(t, err) + require.True(t, eds.Equals(retrievedEDS)) + + // root not found + eh.RawHeader.Height = 666 + _, err = sg.GetEDS(ctx, eh) + require.ErrorIs(t, err, shwap.ErrNotFound) + }) + + t.Run("GetRow", func(t *testing.T) { + eds, roots := randomEDS(t) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + height := height.Add(1) + eh.RawHeader.Height = int64(height) + + err := edsStore.PutODSQ4(ctx, eh.DAH, height, eds) + require.NoError(t, err) + + for i := 0; i < len(eh.DAH.RowRoots); i++ { + row, err := sg.GetRow(ctx, eh, i) + require.NoError(t, err) + retreivedShrs, err := row.Shares() + require.NoError(t, err) + edsShrs, err := libshare.FromBytes(eds.Row(uint(i))) + require.NoError(t, err) + require.Equal(t, edsShrs, retreivedShrs) + } + }) + + t.Run("GetNamespaceData", func(t *testing.T) { + ns := libshare.RandomNamespace() + eds, roots := edstest.RandEDSWithNamespace(t, ns, 8, 16) + eh := headertest.RandExtendedHeaderWithRoot(t, roots) + height := height.Add(1) + eh.RawHeader.Height = int64(height) + err := edsStore.PutODSQ4(ctx, eh.DAH, height, eds) + require.NoError(t, err) + + shares, err := sg.GetNamespaceData(ctx, eh, ns) + require.NoError(t, err) + require.NoError(t, shares.Verify(eh.DAH, ns)) + + // namespace not found + randNamespace := libshare.RandomNamespace() + emptyShares, err := sg.GetNamespaceData(ctx, eh, randNamespace) + require.NoError(t, err) + require.Empty(t, emptyShares.Flatten()) + + // root not found + eh.RawHeader.Height = 666 + _, err = sg.GetNamespaceData(ctx, eh, ns) + require.ErrorIs(t, err, shwap.ErrNotFound) + }) +} diff --git a/store/metrics.go b/store/metrics.go new file mode 100644 index 0000000000..53720493e6 --- /dev/null +++ b/store/metrics.go @@ -0,0 +1,180 @@ +package store + +import ( + "context" + "fmt" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + + "github.com/celestiaorg/celestia-node/store/cache" +) + +const ( + failedKey = "failed" + withQ4Key = "with_q4" + sizeKey = "eds_size" +) + +var meter = otel.Meter("store") + +type metrics struct { + put metric.Float64Histogram + putExists metric.Int64Counter + get metric.Float64Histogram + has metric.Float64Histogram + removeODSQ4 metric.Float64Histogram + removeQ4 metric.Float64Histogram + unreg func() error +} + +func (s *Store) WithMetrics() error { + put, err := meter.Float64Histogram("eds_store_put_time_histogram", + metric.WithDescription("eds store put time histogram(s)")) + if err != nil { + return err + } + + putExists, err := meter.Int64Counter("eds_store_put_exists_counter", + metric.WithDescription("eds store put file exists")) + if err != nil { + return err + } + + get, err := meter.Float64Histogram("eds_store_get_time_histogram", + metric.WithDescription("eds store get time histogram(s)")) + if err != nil { + return err + } + + has, err := meter.Float64Histogram("eds_store_has_time_histogram", + metric.WithDescription("eds store has time histogram(s)")) + if err != nil { + return err + } + + removeQ4, err := meter.Float64Histogram("eds_store_remove_q4_time_histogram", + metric.WithDescription("eds store remove q4 data time histogram(s)")) + if err != nil { + return err + } + + removeODSQ4, err := meter.Float64Histogram("eds_store_remove_odsq4_time_histogram", + metric.WithDescription("eds store remove odsq4 file data time histogram(s)")) + if err != nil { + return err + } + + s.metrics = &metrics{ + put: put, + putExists: putExists, + get: get, + has: has, + removeODSQ4: removeODSQ4, + removeQ4: removeQ4, + } + return s.metrics.addCacheMetrics(s.cache) +} + +// addCacheMetrics adds cache metrics to store metrics +func (m *metrics) addCacheMetrics(c cache.Cache) error { + if m == nil { + return nil + } + unreg, err := c.EnableMetrics() + if err != nil { + return fmt.Errorf("while enabling metrics for cache: %w", err) + } + m.unreg = unreg + return nil +} + +func (m *metrics) observePut( + ctx context.Context, + dur time.Duration, + size uint, + withQ4 bool, + failed bool, +) { + if m == nil { + return + } + if ctx.Err() != nil { + ctx = context.Background() + } + + m.put.Record(ctx, dur.Seconds(), metric.WithAttributes( + attribute.Bool(failedKey, failed), + attribute.Bool(withQ4Key, withQ4), + attribute.Int(sizeKey, int(size)), + ), + ) +} + +func (m *metrics) observePutExist(ctx context.Context) { + if m == nil { + return + } + if ctx.Err() != nil { + ctx = context.Background() + } + + m.putExists.Add(ctx, 1) +} + +func (m *metrics) observeGet(ctx context.Context, dur time.Duration, failed bool) { + if m == nil { + return + } + if ctx.Err() != nil { + ctx = context.Background() + } + + m.get.Record(ctx, dur.Seconds(), metric.WithAttributes( + attribute.Bool(failedKey, failed))) +} + +func (m *metrics) observeHas(ctx context.Context, dur time.Duration, failed bool) { + if m == nil { + return + } + if ctx.Err() != nil { + ctx = context.Background() + } + + m.has.Record(ctx, dur.Seconds(), metric.WithAttributes( + attribute.Bool(failedKey, failed))) +} + +func (m *metrics) observeRemoveODSQ4(ctx context.Context, dur time.Duration, failed bool) { + if m == nil { + return + } + if ctx.Err() != nil { + ctx = context.Background() + } + + m.removeODSQ4.Record(ctx, dur.Seconds(), metric.WithAttributes( + attribute.Bool(failedKey, failed))) +} + +func (m *metrics) observeRemoveQ4(ctx context.Context, dur time.Duration, failed bool) { + if m == nil { + return + } + if ctx.Err() != nil { + ctx = context.Background() + } + + m.removeQ4.Record(ctx, dur.Seconds(), metric.WithAttributes( + attribute.Bool(failedKey, failed))) +} + +func (m *metrics) close() error { + if m == nil { + return nil + } + return m.unreg() +} diff --git a/store/store.go b/store/store.go new file mode 100644 index 0000000000..554337275e --- /dev/null +++ b/store/store.go @@ -0,0 +1,576 @@ +package store + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "strconv" + "time" + + logging "github.com/ipfs/go-log/v2" + + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/libs/utils" + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/store/cache" + "github.com/celestiaorg/celestia-node/store/file" +) + +var log = logging.Logger("edsstore") + +const ( + blocksPath = "blocks" + heightsPath = blocksPath + "/heights" + odsFileExt = ".ods" + q4FileExt = ".q4" + defaultDirPerm = 0o755 +) + +var ErrNotFound = errors.New("eds not found in store") + +// Store is a storage for EDS files. It persists EDS files on disk in form of Q1Q4 files or ODS +// files. It provides methods to put, get and remove EDS files. It has two caches: recent eds cache +// and availability cache. Recent eds cache is used to cache recent blocks. Availability cache is +// used to cache blocks that are accessed by sample requests. Store is thread-safe. +type Store struct { + // basepath is the root directory of the store + basepath string + // cache is used to cache recent blocks and blocks that are accessed frequently + cache cache.Cache + // stripedLocks is used to synchronize parallel operations + stripLock *striplock + metrics *metrics +} + +// NewStore creates a new EDS Store under the given basepath and datastore. +func NewStore(params *Parameters, basePath string) (*Store, error) { + err := params.Validate() + if err != nil { + return nil, err + } + + // ensure the blocks dir exists + blocksDir := filepath.Join(basePath, blocksPath) + if err := mkdir(blocksDir); err != nil { + return nil, fmt.Errorf("ensuring blocks directory: %w", err) + } + + // ensure the heights dir exists + heightsDir := filepath.Join(basePath, heightsPath) + if err := mkdir(heightsDir); err != nil { + return nil, fmt.Errorf("ensuring heights directory: %w", err) + } + + var recentCache cache.Cache = cache.NoopCache{} + if params.RecentBlocksCacheSize > 0 { + recentCache, err = cache.NewAccessorCache("recent", params.RecentBlocksCacheSize) + if err != nil { + return nil, fmt.Errorf("failed to create recent eds cache: %w", err) + } + } + + store := &Store{ + basepath: basePath, + cache: recentCache, + stripLock: newStripLock(1024), + } + + if err := store.populateEmptyFile(); err != nil { + return nil, fmt.Errorf("ensuring empty EDS: %w", err) + } + + return store, nil +} + +func (s *Store) Stop(context.Context) error { + return s.metrics.close() +} + +func (s *Store) PutODSQ4( + ctx context.Context, + roots *share.AxisRoots, + height uint64, + square *rsmt2d.ExtendedDataSquare, +) error { + return s.put(ctx, roots, height, square, true) +} + +func (s *Store) PutODS( + ctx context.Context, + roots *share.AxisRoots, + height uint64, + square *rsmt2d.ExtendedDataSquare, +) error { + return s.put(ctx, roots, height, square, false) +} + +func (s *Store) put( + ctx context.Context, + roots *share.AxisRoots, + height uint64, + square *rsmt2d.ExtendedDataSquare, + writeQ4 bool, +) error { + datahash := share.DataHash(roots.Hash()) + // we don't need to store empty EDS, just link the height to the empty file + if datahash.IsEmptyEDS() { + lock := s.stripLock.byHeight(height) + lock.Lock() + defer lock.Unlock() + err := s.linkHeight(datahash, height) + if errors.Is(err, os.ErrExist) { + return nil + } + return err + } + + // put to cache before writing to make it accessible while write is happening + accessor := &eds.Rsmt2D{ExtendedDataSquare: square} + acc, err := s.cache.GetOrLoad(ctx, height, accessorLoader(accessor)) + if err != nil { + log.Warnf("failed to put Accessor in the recent cache: %s", err) + } else { + // release the ref link to the accessor + utils.CloseAndLog(log, "recent accessor", acc) + } + + tNow := time.Now() + lock := s.stripLock.byHashAndHeight(datahash, height) + lock.lock() + defer lock.unlock() + + var exists bool + if writeQ4 { + exists, err = s.createODSQ4File(square, roots, height) + } else { + exists, err = s.createODSFile(square, roots, height) + } + + if exists { + s.metrics.observePutExist(ctx) + return nil + } + if err != nil { + s.metrics.observePut(ctx, time.Since(tNow), square.Width(), writeQ4, true) + return fmt.Errorf("creating file: %w", err) + } + + s.metrics.observePut(ctx, time.Since(tNow), square.Width(), writeQ4, false) + return nil +} + +func (s *Store) createODSQ4File( + square *rsmt2d.ExtendedDataSquare, + roots *share.AxisRoots, + height uint64, +) (bool, error) { + pathODS := s.hashToPath(roots.Hash(), odsFileExt) + pathQ4 := s.hashToPath(roots.Hash(), q4FileExt) + + err := file.CreateODSQ4(pathODS, pathQ4, roots, square) + if err != nil && !errors.Is(err, os.ErrExist) { + // ensure we don't have partial writes if any operation fails + removeErr := s.removeODSQ4(height, roots.Hash()) + return false, errors.Join( + fmt.Errorf("creating ODSQ4 file: %w", err), + removeErr, + ) + } + + // if file already exists, check if it's corrupted + if errors.Is(err, os.ErrExist) { + err = s.validateAndRecoverODSQ4(square, roots, height, pathODS, pathQ4) + if err != nil { + return false, err + } + } + + // create hard link with height as name + err = s.linkHeight(roots.Hash(), height) + // if both file and link exist, we consider it as success + if errors.Is(err, os.ErrExist) { + return true, nil + } + if err != nil { + // ensure we don't have partial writes if any operation fails + removeErr := s.removeODSQ4(height, roots.Hash()) + return false, errors.Join( + fmt.Errorf("hardlinking height: %w", err), + removeErr, + ) + } + return false, nil +} + +func (s *Store) validateAndRecoverODSQ4( + square *rsmt2d.ExtendedDataSquare, + roots *share.AxisRoots, + height uint64, + pathODS, pathQ4 string, +) error { + // Validate the size of the file to ensure it's not corrupted + err := file.ValidateODSQ4Size(pathODS, pathQ4, square) + if err == nil { + return nil + } + log.Warnf("ODSQ4 file with height %d is corrupted, recovering", height) + err = s.removeODSQ4(height, roots.Hash()) + if err != nil { + return fmt.Errorf("removing corrupted ODSQ4 file: %w", err) + } + err = file.CreateODSQ4(pathODS, pathQ4, roots, square) + if err != nil { + return fmt.Errorf("recreating ODSQ4 file: %w", err) + } + return nil +} + +func (s *Store) createODSFile( + square *rsmt2d.ExtendedDataSquare, + roots *share.AxisRoots, + height uint64, +) (bool, error) { + pathODS := s.hashToPath(roots.Hash(), odsFileExt) + err := file.CreateODS(pathODS, roots, square) + if err != nil && !errors.Is(err, os.ErrExist) { + // ensure we don't have partial writes if any operation fails + removeErr := s.removeODS(height, roots.Hash()) + return false, errors.Join( + fmt.Errorf("creating ODS file: %w", err), + removeErr, + ) + } + + // if file already exists, check if it's corrupted + if errors.Is(err, os.ErrExist) { + // Validate the size of the file to ensure it's not corrupted + err = s.validateAndRecoverODS(square, roots, height, pathODS) + if err != nil { + return false, err + } + } + + // create hard link with height as name + err = s.linkHeight(roots.Hash(), height) + // if both file and link exist, we consider it as success + if errors.Is(err, os.ErrExist) { + return true, nil + } + if err != nil { + // ensure we don't have partial writes if any operation fails + removeErr := s.removeODS(height, roots.Hash()) + return false, errors.Join( + fmt.Errorf("hardlinking height: %w", err), + removeErr, + ) + } + return false, nil +} + +func (s *Store) validateAndRecoverODS( + square *rsmt2d.ExtendedDataSquare, + roots *share.AxisRoots, + height uint64, + pathODS string, +) error { + // Validate the size of the file to ensure it's not corrupted + err := file.ValidateODSSize(pathODS, square) + if err == nil { + return nil + } + log.Warnf("ODS file with height %d is corrupted, recovering", height) + err = s.removeODS(height, roots.Hash()) + if err != nil { + return fmt.Errorf("removing corrupted ODS file: %w", err) + } + err = file.CreateODS(pathODS, roots, square) + if err != nil { + return fmt.Errorf("recreating ODS file: %w", err) + } + return nil +} + +func (s *Store) linkHeight(datahash share.DataHash, height uint64) error { + linktoOds := s.heightToPath(height, odsFileExt) + if datahash.IsEmptyEDS() { + // empty EDS is always symlinked, because there is limited number of hardlinks + // for the same file in some filesystems (ext4) + pathOds := s.hashToRelativePath(datahash, odsFileExt) + return symlink(pathOds, linktoOds) + } + // create hard link with height as name + pathOds := s.hashToPath(datahash, odsFileExt) + return hardLink(pathOds, linktoOds) +} + +// populateEmptyFile writes fresh empty EDS file on disk. +// It overrides existing empty file to ensure disk format is always consistent with the canonical +// in-mem representation. +func (s *Store) populateEmptyFile() error { + pathOds := s.hashToPath(share.EmptyEDSDataHash(), odsFileExt) + pathQ4 := s.hashToPath(share.EmptyEDSDataHash(), q4FileExt) + + err := errors.Join(remove(pathOds), remove(pathQ4)) + if err != nil { + return fmt.Errorf("cleaning old empty EDS file: %w", err) + } + + err = file.CreateODSQ4(pathOds, pathQ4, share.EmptyEDSRoots(), eds.EmptyAccessor.ExtendedDataSquare) + if err != nil { + return fmt.Errorf("creating fresh empty EDS file: %w", err) + } + + return nil +} + +func (s *Store) GetByHash(ctx context.Context, datahash share.DataHash) (eds.AccessorStreamer, error) { + if datahash.IsEmptyEDS() { + return eds.EmptyAccessor, nil + } + lock := s.stripLock.byHash(datahash) + lock.RLock() + defer lock.RUnlock() + + tNow := time.Now() + f, err := s.getByHash(ctx, datahash) + s.metrics.observeGet(ctx, time.Since(tNow), err != nil) + return f, err +} + +func (s *Store) getByHash(ctx context.Context, datahash share.DataHash) (eds.AccessorStreamer, error) { + path := s.hashToPath(datahash, odsFileExt) + return s.openAccessor(ctx, path) +} + +func (s *Store) GetByHeight(ctx context.Context, height uint64) (eds.AccessorStreamer, error) { + lock := s.stripLock.byHeight(height) + lock.RLock() + defer lock.RUnlock() + + tNow := time.Now() + f, err := s.getByHeight(ctx, height) + s.metrics.observeGet(ctx, time.Since(tNow), err != nil) + return f, err +} + +func (s *Store) getByHeight(ctx context.Context, height uint64) (eds.AccessorStreamer, error) { + f, err := s.cache.Get(height) + if err == nil { + return f, nil + } + + path := s.heightToPath(height, odsFileExt) + return s.openAccessor(ctx, path) +} + +// openAccessor opens ODSQ4 Accessor. +// It opens ODS file first, reads up its DataHash and constructs the path for Q4 +// This done as Q4 is not indexed(hard-linked) and there is no other way to Q4 by height only. +func (s *Store) openAccessor(ctx context.Context, path string) (eds.AccessorStreamer, error) { + ods, err := file.OpenODS(path) + if errors.Is(err, os.ErrNotExist) { + return nil, ErrNotFound + } + if err != nil { + return nil, fmt.Errorf("failed to open ODS: %w", err) + } + + // read datahash from ODS and construct Q4 path + datahash, err := ods.DataHash(ctx) + if err != nil { + utils.CloseAndLog(log, "open ods", ods) + return nil, fmt.Errorf("reading datahash: %w", err) + } + pathQ4 := s.hashToPath(datahash, q4FileExt) + odsQ4 := file.ODSWithQ4(ods, pathQ4) + return wrapAccessor(odsQ4), nil +} + +func (s *Store) HasByHash(ctx context.Context, datahash share.DataHash) (bool, error) { + if datahash.IsEmptyEDS() { + return true, nil + } + + lock := s.stripLock.byHash(datahash) + lock.RLock() + defer lock.RUnlock() + + tNow := time.Now() + exist, err := s.hasByHash(datahash) + s.metrics.observeHas(ctx, time.Since(tNow), err != nil) + return exist, err +} + +func (s *Store) hasByHash(datahash share.DataHash) (bool, error) { + // For now, we assume that if ODS exists, the Q4 exists as well. + path := s.hashToPath(datahash, odsFileExt) + return exists(path) +} + +func (s *Store) HasByHeight(ctx context.Context, height uint64) (bool, error) { + lock := s.stripLock.byHeight(height) + lock.RLock() + defer lock.RUnlock() + + tNow := time.Now() + exist, err := s.hasByHeight(height) + s.metrics.observeHas(ctx, time.Since(tNow), err != nil) + return exist, err +} + +func (s *Store) hasByHeight(height uint64) (bool, error) { + if s.cache.Has(height) { + return true, nil + } + + // For now, we assume that if ODS exists, the Q4 exists as well. + pathODS := s.heightToPath(height, odsFileExt) + return exists(pathODS) +} + +func (s *Store) RemoveODSQ4(ctx context.Context, height uint64, datahash share.DataHash) error { + lock := s.stripLock.byHashAndHeight(datahash, height) + lock.lock() + defer lock.unlock() + + tNow := time.Now() + err := s.removeODSQ4(height, datahash) + s.metrics.observeRemoveODSQ4(ctx, time.Since(tNow), err != nil) + return err +} + +func (s *Store) removeODSQ4(height uint64, datahash share.DataHash) error { + if err := s.removeODS(height, datahash); err != nil { + return fmt.Errorf("removing ODS: %w", err) + } + if err := s.removeQ4(height, datahash); err != nil { + return fmt.Errorf("removing Q4: %w", err) + } + return nil +} + +func (s *Store) removeODS(height uint64, datahash share.DataHash) error { + if err := s.cache.Remove(height); err != nil { + return fmt.Errorf("removing from cache: %w", err) + } + + pathLink := s.heightToPath(height, odsFileExt) + if err := remove(pathLink); err != nil { + return fmt.Errorf("removing hardlink: %w", err) + } + + // if datahash is empty, we don't need to remove the ODS file, only the hardlink + if datahash.IsEmptyEDS() { + return nil + } + + pathODS := s.hashToPath(datahash, odsFileExt) + if err := remove(pathODS); err != nil { + return fmt.Errorf("removing ODS file: %w", err) + } + return nil +} + +func (s *Store) RemoveQ4(ctx context.Context, height uint64, datahash share.DataHash) error { + lock := s.stripLock.byHashAndHeight(datahash, height) + lock.lock() + defer lock.unlock() + + tNow := time.Now() + err := s.removeQ4(height, datahash) + s.metrics.observeRemoveQ4(ctx, time.Since(tNow), err != nil) + return err +} + +func (s *Store) removeQ4(height uint64, datahash share.DataHash) error { + // if datahash is empty, we don't need to remove the Q4 file + if datahash.IsEmptyEDS() { + return nil + } + + if err := s.cache.Remove(height); err != nil { + return fmt.Errorf("removing from cache: %w", err) + } + + // remove Q4 file + pathQ4File := s.hashToPath(datahash, q4FileExt) + if err := remove(pathQ4File); err != nil { + return fmt.Errorf("removing Q4 file: %w", err) + } + return nil +} + +func (s *Store) hashToPath(datahash share.DataHash, ext string) string { + return filepath.Join(s.basepath, blocksPath, datahash.String()) + ext +} + +func (s *Store) hashToRelativePath(datahash share.DataHash, ext string) string { + return filepath.Join("..", datahash.String()) + ext +} + +func (s *Store) heightToPath(height uint64, ext string) string { + return filepath.Join(s.basepath, heightsPath, strconv.Itoa(int(height))) + ext +} + +func accessorLoader(accessor eds.AccessorStreamer) cache.OpenAccessorFn { + return func(context.Context) (eds.AccessorStreamer, error) { + return wrapAccessor(accessor), nil + } +} + +func wrapAccessor(accessor eds.AccessorStreamer) eds.AccessorStreamer { + withCache := eds.WithProofsCache(accessor) + closedOnce := eds.WithClosedOnce(withCache) + sanityChecked := eds.WithValidation(closedOnce) + accessorStreamer := eds.AccessorAndStreamer(sanityChecked, closedOnce) + return accessorStreamer +} + +func mkdir(path string) error { + err := os.Mkdir(path, defaultDirPerm) + if err != nil && !errors.Is(err, os.ErrExist) { + return fmt.Errorf("making directory '%s': %w", path, err) + } + + return nil +} + +func hardLink(filepath, linkpath string) error { + err := os.Link(filepath, linkpath) + if err != nil { + return fmt.Errorf("creating hardlink (%s -> %s): %w", filepath, linkpath, err) + } + return nil +} + +func symlink(filepath, linkpath string) error { + err := os.Symlink(filepath, linkpath) + if err != nil { + return fmt.Errorf("creating symlink (%s -> %s): %w", filepath, linkpath, err) + } + return nil +} + +func exists(path string) (bool, error) { + _, err := os.Stat(path) + switch { + case err == nil: + return true, nil + case errors.Is(err, os.ErrNotExist): + return false, nil + default: + return false, fmt.Errorf("checking file existence '%s': %w", path, err) + } +} + +func remove(path string) error { + err := os.Remove(path) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("removing file '%s': %w", path, err) + } + return nil +} diff --git a/store/store_cache.go b/store/store_cache.go new file mode 100644 index 0000000000..9e552312fd --- /dev/null +++ b/store/store_cache.go @@ -0,0 +1,66 @@ +package store + +import ( + "context" + "fmt" + + "github.com/celestiaorg/celestia-node/share/eds" + "github.com/celestiaorg/celestia-node/store/cache" +) + +// CachedStore is a store with an additional cache layer. New cache layer is created on top of the +// original store cache. Parent store cache will be able to read from the new cache layer, but will +// not be able to write to it. Making parent store cache and CachedStore cache independent for writes. +type CachedStore struct { + store *Store + combinedCache *cache.DoubleCache +} + +// WithCache wraps store with extra layer of cache. Created caching layer will have read access to original +// store cache and will duplicate it's content. It updates parent store cache, to allow it to +// read from additionally created cache layer. +func (s *Store) WithCache(name string, size int) (*CachedStore, error) { + if size <= 0 { + return nil, fmt.Errorf("cache size must be positive, got %d", size) + } + newCache, err := cache.NewAccessorCache(name, size) + if err != nil { + return nil, fmt.Errorf("failed to create %s cache: %w", name, err) + } + + wrappedCache := cache.NewDoubleCache(s.cache, newCache) + err = s.metrics.addCacheMetrics(wrappedCache) + if err != nil { + return nil, fmt.Errorf("failed to add cache metrics: %w", err) + } + // update parent store cache to allow it to read from both caches + s.cache = wrappedCache + return &CachedStore{ + store: s, + combinedCache: wrappedCache, + }, nil +} + +// HasByHeight checks if accessor for the height is present. +func (cs *CachedStore) HasByHeight(ctx context.Context, height uint64) (bool, error) { + // store checks the combinedCache itself, so we can simply passthrough the call + return cs.store.HasByHeight(ctx, height) +} + +// GetByHeight returns accessor for given height and puts it into cache. +func (cs *CachedStore) GetByHeight(ctx context.Context, height uint64) (eds.AccessorStreamer, error) { + acc, err := cs.combinedCache.First().Get(height) + if err == nil { + return acc, nil + } + return cs.combinedCache.Second().GetOrLoad(ctx, height, cs.openFile(height)) +} + +func (cs *CachedStore) openFile(height uint64) cache.OpenAccessorFn { + return func(ctx context.Context) (eds.AccessorStreamer, error) { + // open file directly without calling GetByHeight of inner getter to + // avoid hitting store cache second time + path := cs.store.heightToPath(height, odsFileExt) + return cs.store.openAccessor(ctx, path) + } +} diff --git a/store/store_cache_test.go b/store/store_cache_test.go new file mode 100644 index 0000000000..fdfe01a550 --- /dev/null +++ b/store/store_cache_test.go @@ -0,0 +1,108 @@ +package store + +import ( + "context" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/celestia-node/store/cache" +) + +func TestStore_WithCache(t *testing.T) { + height := atomic.Uint64{} + height.Store(1) + + t.Run("don't exist in first cache", func(t *testing.T) { + // create store with no cache + params := paramsNoCache() + store, err := NewStore(params, t.TempDir()) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + eds, roots := randomEDS(t) + height := height.Add(1) + err = store.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + // check that the height is not in the cache (cache was disabled) + _, err = store.cache.Get(height) + require.ErrorIs(t, err, cache.ErrCacheMiss) + + cachedStore, err := store.WithCache("test", 10) + require.NoError(t, err) + // load accessor to secondary cache by calling GetByHeight on cached store + acc, err := cachedStore.GetByHeight(ctx, height) + require.NoError(t, err) + require.NoError(t, acc.Close()) + + // loaded accessor should be available in both original store and wrapped store + acc, err = store.cache.Get(height) + require.NoError(t, err) + require.NoError(t, acc.Close()) + acc, err = cachedStore.combinedCache.Get(height) + require.NoError(t, err) + require.NoError(t, acc.Close()) + }) + + t.Run("exists in first cache", func(t *testing.T) { + store, err := NewStore(DefaultParameters(), t.TempDir()) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + eds, roots := randomEDS(t) + height := height.Add(1) + err = store.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + acc, err := store.cache.Get(height) + require.NoError(t, err) + require.NoError(t, acc.Close()) + + withCache, err := store.WithCache("test", 10) + require.NoError(t, err) + acc, err = withCache.GetByHeight(ctx, height) + require.NoError(t, err) + require.NoError(t, acc.Close()) + + _, err = withCache.combinedCache.Second().Get(height) + require.ErrorIs(t, err, cache.ErrCacheMiss) + }) + + t.Run("Has", func(t *testing.T) { + store, err := NewStore(DefaultParameters(), t.TempDir()) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + eds, roots := randomEDS(t) + height := height.Add(1) + err = store.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + withCache, err := store.WithCache("test", 10) + require.NoError(t, err) + + has, err := withCache.HasByHeight(ctx, height) + require.NoError(t, err) + require.True(t, has) + + // load up into cache + acc, err := withCache.GetByHeight(ctx, height) + require.NoError(t, err) + require.NoError(t, acc.Close()) + + has = withCache.combinedCache.Has(height) + require.True(t, has) + }) +} + +func paramsNoCache() *Parameters { + params := DefaultParameters() + params.RecentBlocksCacheSize = 0 + return params +} diff --git a/store/store_options.go b/store/store_options.go new file mode 100644 index 0000000000..68299db7f1 --- /dev/null +++ b/store/store_options.go @@ -0,0 +1,24 @@ +package store + +import ( + "errors" +) + +type Parameters struct { + // RecentBlocksCacheSize is the size of the cache for recent blocks. + RecentBlocksCacheSize int +} + +// DefaultParameters returns the default configuration values for the EDS store parameters. +func DefaultParameters() *Parameters { + return &Parameters{ + RecentBlocksCacheSize: 10, + } +} + +func (p *Parameters) Validate() error { + if p.RecentBlocksCacheSize < 0 { + return errors.New("recent eds cache size cannot be negative") + } + return nil +} diff --git a/store/store_test.go b/store/store_test.go new file mode 100644 index 0000000000..4374e01bbf --- /dev/null +++ b/store/store_test.go @@ -0,0 +1,526 @@ +package store + +import ( + "context" + "os" + "path" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" + + libshare "github.com/celestiaorg/go-square/v2/share" + "github.com/celestiaorg/rsmt2d" + + "github.com/celestiaorg/celestia-node/share" + "github.com/celestiaorg/celestia-node/share/eds/edstest" + "github.com/celestiaorg/celestia-node/store/cache" + "github.com/celestiaorg/celestia-node/store/file" +) + +func TestEDSStore(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + t.Cleanup(cancel) + + height := atomic.Uint64{} + height.Store(1) + + putTests := []struct { + name string + newEds func(t testing.TB) (*rsmt2d.ExtendedDataSquare, *share.AxisRoots) + putFn func(*Store) putFunc + addedFiles int + addedLinks int + filesAfterRemoveQ4 int + }{ + { + name: "ODS, non empty eds", + newEds: randomEDS, + putFn: func(store *Store) putFunc { + return store.PutODS + }, + addedFiles: 1, + addedLinks: 1, + filesAfterRemoveQ4: 1, + }, + { + name: "ODS, empty eds", + newEds: func(t testing.TB) (*rsmt2d.ExtendedDataSquare, *share.AxisRoots) { + return share.EmptyEDS(), share.EmptyEDSRoots() + }, + putFn: func(store *Store) putFunc { + return store.PutODS + }, + addedFiles: 0, + addedLinks: 1, + filesAfterRemoveQ4: 0, + }, + { + name: "ODSQ4, non empty eds", + newEds: randomEDS, + putFn: func(store *Store) putFunc { + return store.PutODSQ4 + }, + addedFiles: 2, + addedLinks: 1, + filesAfterRemoveQ4: 1, + }, + { + name: "ODSQ4, empty eds", + newEds: func(t testing.TB) (*rsmt2d.ExtendedDataSquare, *share.AxisRoots) { + return share.EmptyEDS(), share.EmptyEDSRoots() + }, + putFn: func(store *Store) putFunc { + return store.PutODSQ4 + }, + addedFiles: 0, + addedLinks: 1, + filesAfterRemoveQ4: 0, + }, + } + + for _, test := range putTests { + t.Run(test.name, func(t *testing.T) { + t.Run("Put", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + eds, roots := test.newEds(t) + height := height.Add(1) + + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + + // file should exist in the store + hasByHashAndHeight(t, edsStore, ctx, roots.Hash(), height, true, true) + + // block folder should contain the correct amount of files and links + ensureAmountFileAndLinks(t, dir, test.addedFiles, test.addedLinks) + }) + + t.Run("Cached after Put", func(t *testing.T) { + eds, roots := test.newEds(t) + if share.DataHash(roots.Hash()).IsEmptyEDS() { + // skip test, empty eds is not cached after put + t.Skip() + } + + edsStore, err := NewStore(DefaultParameters(), t.TempDir()) + require.NoError(t, err) + + height := height.Add(1) + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + + // non-empty file should be cached after put + f, err := edsStore.cache.Get(height) + require.NoError(t, err) + require.NoError(t, f.Close()) + // check that cached file is the same eds + fromFile, err := f.Shares(ctx) + require.NoError(t, err) + require.NoError(t, f.Close()) + expected := eds.FlattenedODS() + require.Equal(t, expected, libshare.ToBytes(fromFile)) + }) + }) + + t.Run("Second Put should be noop", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + eds, roots := test.newEds(t) + height := height.Add(1) + + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + // ensure correct amount of files and links are written + ensureAmountFileAndLinks(t, dir, test.addedFiles, test.addedLinks) + + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + + // ensure no new files or links are written + ensureAmountFileAndLinks(t, dir, test.addedFiles, test.addedLinks) + }) + + t.Run("Second Put after partial write", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + eds, roots := test.newEds(t) + height := height.Add(1) + + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + // remove link + pathLink := edsStore.heightToPath(height, odsFileExt) + err = remove(pathLink) + require.NoError(t, err) + ensureAmountLinks(t, dir, 0) + + // put should write the missing link + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + ensureAmountLinks(t, dir, test.addedLinks) + }) + + t.Run("RemoveODSQ4", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(DefaultParameters(), dir) + require.NoError(t, err) + + eds, roots := test.newEds(t) + height := height.Add(1) + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + ensureAmountFileAndLinks(t, dir, test.addedFiles, test.addedLinks) + + hash := share.DataHash(roots.Hash()) + err = edsStore.RemoveODSQ4(ctx, height, hash) + require.NoError(t, err) + + // file should be removed from cache + _, err = edsStore.cache.Get(height) + require.ErrorIs(t, err, cache.ErrCacheMiss) + + // empty file should be accessible by hash, non-empty file should not + hasByHash := hash.IsEmptyEDS() + // all files should not be accessible by height + hasByHashAndHeight(t, edsStore, ctx, hash, height, hasByHash, false) + + // ensure all files and links are removed + ensureAmountFileAndLinks(t, dir, 0, 0) + }) + + t.Run("RemoveQ4", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(DefaultParameters(), dir) + require.NoError(t, err) + + eds, roots := test.newEds(t) + height := height.Add(1) + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + ensureAmountFileAndLinks(t, dir, test.addedFiles, test.addedLinks) + + hash := share.DataHash(roots.Hash()) + err = edsStore.RemoveQ4(ctx, height, hash) + require.NoError(t, err) + + // file should be removed from cache + _, err = edsStore.cache.Get(height) + require.ErrorIs(t, err, cache.ErrCacheMiss) + + // ods file should still be accessible by hash and height + hasByHashAndHeight(t, edsStore, ctx, hash, height, true, true) + + // ensure ods file and link are not removed + ensureAmountFileAndLinks(t, dir, test.filesAfterRemoveQ4, test.addedLinks) + }) + + t.Run("GetByHeight", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(DefaultParameters(), dir) + require.NoError(t, err) + + eds, roots := test.newEds(t) + height := height.Add(1) + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + ensureAmountFileAndLinks(t, dir, test.addedFiles, test.addedLinks) + + f, err := edsStore.GetByHeight(ctx, height) + require.NoError(t, err) + + // check that file is the same eds + fromFile, err := f.Shares(ctx) + require.NoError(t, err) + require.NoError(t, f.Close()) + expected := eds.FlattenedODS() + require.Equal(t, expected, libshare.ToBytes(fromFile)) + }) + + t.Run("GetByHash", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(DefaultParameters(), dir) + require.NoError(t, err) + + eds, roots := test.newEds(t) + height := height.Add(1) + err = test.putFn(edsStore)(ctx, roots, height, eds) + require.NoError(t, err) + ensureAmountFileAndLinks(t, dir, test.addedFiles, test.addedLinks) + + f, err := edsStore.GetByHash(ctx, roots.Hash()) + require.NoError(t, err) + + // check that cached file is the same eds + fromFile, err := f.Shares(ctx) + require.NoError(t, err) + require.NoError(t, f.Close()) + expected := eds.FlattenedODS() + require.Equal(t, expected, libshare.ToBytes(fromFile)) + }) + } + + t.Run("Does not exist", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + _, roots := randomEDS(t) + // file does not exist + hasByHashAndHeight(t, edsStore, ctx, roots.Hash(), 1, false, false) + + _, err = edsStore.GetByHeight(ctx, 1) + require.ErrorIs(t, err, ErrNotFound) + + _, err = edsStore.GetByHash(ctx, roots.Hash()) + require.ErrorIs(t, err, ErrNotFound) + }) + + t.Run("empty EDS returned by hash", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + // assert that the empty file exists + roots := share.EmptyEDSRoots() + has, err := edsStore.HasByHash(ctx, roots.Hash()) + require.NoError(t, err) + require.True(t, has) + + // assert that the empty file is, in fact, empty + f, err := edsStore.GetByHash(ctx, roots.Hash()) + require.NoError(t, err) + hash, err := f.DataHash(ctx) + require.NoError(t, err) + require.True(t, hash.IsEmptyEDS()) + }) + + t.Run("reopen", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + // tests that store can be reopened + err = edsStore.Stop(ctx) + require.NoError(t, err) + + _, err = NewStore(paramsNoCache(), dir) + require.NoError(t, err) + }) + + t.Run("recover ODS", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + eds, roots := randomEDS(t) + height := height.Add(1) + err = edsStore.PutODS(ctx, roots, height, eds) + require.NoError(t, err) + + // corrupt ODS file + pathODS := edsStore.heightToPath(height, odsFileExt) + err = corruptFile(pathODS) + require.NoError(t, err) + + // check if file is corrupted + err = file.ValidateODSSize(pathODS, eds) + require.Error(t, err) + + // second put should recover the file + err = edsStore.PutODS(ctx, roots, height, eds) + require.NoError(t, err) + + // check if file is recovered + err = file.ValidateODSSize(pathODS, eds) + require.NoError(t, err) + }) + + t.Run("recover ODSQ4", func(t *testing.T) { + dir := t.TempDir() + edsStore, err := NewStore(paramsNoCache(), dir) + require.NoError(t, err) + + t.Run("corrupt ODS file", func(t *testing.T) { + eds, roots := randomEDS(t) + height := height.Add(1) + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + // corrupt ODS file + pathODS := edsStore.heightToPath(height, odsFileExt) + err := corruptFile(pathODS) + require.NoError(t, err) + + // check if file is corrupted + pathQ4 := edsStore.hashToPath(roots.Hash(), q4FileExt) + err = file.ValidateODSQ4Size(pathODS, pathQ4, eds) + require.Error(t, err) + + // second put should recover the file + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + // check if file is recovered + err = file.ValidateODSQ4Size(pathODS, pathQ4, eds) + require.NoError(t, err) + }) + + t.Run("corrupt Q4 file", func(t *testing.T) { + eds, roots := randomEDS(t) + height := height.Add(1) + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + // corrupt Q4 file + pathQ4 := edsStore.hashToPath(roots.Hash(), q4FileExt) + err := corruptFile(pathQ4) + require.NoError(t, err) + + // check if file is corrupted + pathODS := edsStore.heightToPath(height, odsFileExt) + err = file.ValidateODSQ4Size(pathODS, pathQ4, eds) + require.Error(t, err) + + // second put should recover the file + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(t, err) + + // check if file is recovered + err = file.ValidateODSQ4Size(pathODS, pathQ4, eds) + require.NoError(t, err) + }) + }) +} + +func corruptFile(path string) error { + file, err := os.OpenFile(path, os.O_RDWR, 0) + if err != nil { + return err + } + defer file.Close() + info, err := file.Stat() + if err != nil { + return err + } + return file.Truncate(info.Size() - 1) +} + +func BenchmarkStore(b *testing.B) { + ctx, cancel := context.WithCancel(context.Background()) + b.Cleanup(cancel) + + eds := edstest.RandEDS(b, 128) + roots, err := share.NewAxisRoots(eds) + require.NoError(b, err) + + // BenchmarkStore/put_128-16 186 6623266 ns/op + b.Run("put 128", func(b *testing.B) { + edsStore, err := NewStore(paramsNoCache(), b.TempDir()) + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + roots := edstest.RandomAxisRoots(b, 1) + _ = edsStore.PutODSQ4(ctx, roots, uint64(i), eds) + } + }) + + // read 128 EDSs does not read full EDS, but only the header + // BenchmarkStore/open_by_height,_128-16 1585693 747.6 ns/op (~7mcs) + b.Run("open by height, 128", func(b *testing.B) { + edsStore, err := NewStore(paramsNoCache(), b.TempDir()) + require.NoError(b, err) + + height := uint64(1984) + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + f, err := edsStore.GetByHeight(ctx, height) + require.NoError(b, err) + require.NoError(b, f.Close()) + } + }) + + // BenchmarkStore/open_by_hash,_128-16 1240942 945.9 ns/op (~9mcs) + b.Run("open by hash, 128", func(b *testing.B) { + edsStore, err := NewStore(DefaultParameters(), b.TempDir()) + require.NoError(b, err) + + height := uint64(1984) + err = edsStore.PutODSQ4(ctx, roots, height, eds) + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + f, err := edsStore.GetByHash(ctx, roots.Hash()) + require.NoError(b, err) + require.NoError(b, f.Close()) + } + }) +} + +func randomEDS(t testing.TB) (*rsmt2d.ExtendedDataSquare, *share.AxisRoots) { + eds := edstest.RandEDS(t, 4) + roots, err := share.NewAxisRoots(eds) + require.NoError(t, err) + + return eds, roots +} + +func ensureAmountFileAndLinks(t testing.TB, dir string, files, links int) { + ensureAmountFiles(t, dir, files) + ensureAmountLinks(t, dir, links) +} + +func ensureAmountFiles(t testing.TB, dir string, files int) { + // add empty file ods and q4 parts and heights folder to the count + files += 3 + // ensure block folder contains the correct amount of files + blockPath := path.Join(dir, blocksPath) + entries, err := os.ReadDir(blockPath) + require.NoError(t, err) + require.Len(t, entries, files) +} + +func ensureAmountLinks(t testing.TB, dir string, links int) { + // ensure heights folder contains the correct amount of links + linksPath := path.Join(dir, heightsPath) + entries, err := os.ReadDir(linksPath) + require.NoError(t, err) + require.Len(t, entries, links) +} + +func hasByHashAndHeight( + t testing.TB, + store *Store, + ctx context.Context, + hash share.DataHash, + height uint64, + hasByHash, hasByHeight bool, +) { + has, err := store.HasByHash(ctx, hash) + require.NoError(t, err) + require.Equal(t, hasByHash, has) + + has, err = store.HasByHeight(ctx, height) + require.NoError(t, err) + require.Equal(t, hasByHeight, has) +} + +type putFunc func( + ctx context.Context, + roots *share.AxisRoots, + height uint64, + square *rsmt2d.ExtendedDataSquare, +) error diff --git a/store/striplock.go b/store/striplock.go new file mode 100644 index 0000000000..4738453c77 --- /dev/null +++ b/store/striplock.go @@ -0,0 +1,55 @@ +package store + +import ( + "sync" + + "github.com/celestiaorg/celestia-node/share" +) + +// TODO: move to utils +type striplock struct { + heights []*sync.RWMutex + datahashes []*sync.RWMutex +} + +type multiLock struct { + mu []*sync.RWMutex +} + +func newStripLock(size int) *striplock { + heights := make([]*sync.RWMutex, size) + datahashes := make([]*sync.RWMutex, size) + for i := 0; i < size; i++ { + heights[i] = &sync.RWMutex{} + datahashes[i] = &sync.RWMutex{} + } + return &striplock{heights, datahashes} +} + +func (l *striplock) byHeight(height uint64) *sync.RWMutex { + lkIdx := height % uint64(len(l.heights)) + return l.heights[lkIdx] +} + +func (l *striplock) byHash(datahash share.DataHash) *sync.RWMutex { + // Use the last 2 bytes of the hash as key to distribute the locks + last := uint16(datahash[len(datahash)-1]) | uint16(datahash[len(datahash)-2])<<8 + lkIdx := last % uint16(len(l.datahashes)) + return l.datahashes[lkIdx] +} + +func (l *striplock) byHashAndHeight(datahash share.DataHash, height uint64) *multiLock { + return &multiLock{[]*sync.RWMutex{l.byHash(datahash), l.byHeight(height)}} +} + +func (m *multiLock) lock() { + for _, lk := range m.mu { + lk.Lock() + } +} + +func (m *multiLock) unlock() { + for _, lk := range m.mu { + lk.Unlock() + } +}