pax_global_header00006660000000000000000000000064145512310520014510gustar00rootroot0000000000000052 comment=addbdbd910e5fdad3de58a210eb0990a6e328775 golang-github-transparency-dev-merkle-0.0.2/000077500000000000000000000000001455123105200207765ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/.clusterfuzzlite/000077500000000000000000000000001455123105200243325ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/.clusterfuzzlite/Dockerfile000066400000000000000000000004071455123105200263250ustar00rootroot00000000000000# https://google.github.io/clusterfuzzlite/build-integration/#dockerfile FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:c7f01a616db0fc554d98edbd7d49a7177197f77fd4e1371bcb7774d7eac8216d COPY . $SRC/merkle WORKDIR $SRC/merkle COPY .clusterfuzzlite/build.sh $SRC/ golang-github-transparency-dev-merkle-0.0.2/.clusterfuzzlite/build.sh000066400000000000000000000027251455123105200257730ustar00rootroot00000000000000# https://google.github.io/oss-fuzz/getting-started/new-project-guide/go-lang/#buildsh # undocumented dependency go install github.com/AdamKorcz/go-118-fuzz-build@c5484365413eb6c532d2dbd0d16b553988ce6852 go get github.com/AdamKorcz/go-118-fuzz-build/testing@c5484365413eb6c532d2dbd0d16b553988ce6852 # workaround https://github.com/AdamKorcz/go-118-fuzz-build/issues/2 mv testonly/constants.go testonly/constants_fuzz.go mv testonly/reference_test.go testonly/reference_test_fuzz.go mv testonly/tree_test.go testonly/tree_test_fuzz.go mv testonly/tree.go testonly/tree_fuzz.go # necessary to list each fuzz test explicitly compile_native_go_fuzzer github.com/transparency-dev/merkle/compact FuzzRangeNodes FuzzRangeNodes compile_native_go_fuzzer github.com/transparency-dev/merkle/testonly FuzzConsistencyProofAndVerify FuzzConsistencyProofAndVerify compile_native_go_fuzzer github.com/transparency-dev/merkle/testonly FuzzInclusionProofAndVerify FuzzInclusionProofAndVerify compile_native_go_fuzzer github.com/transparency-dev/merkle/testonly FuzzHashAtAgainstReferenceImplementation FuzzHashAtAgainstReferenceImplementation compile_native_go_fuzzer github.com/transparency-dev/merkle/testonly FuzzInclusionProofAgainstReferenceImplementation FuzzInclusionProofAgainstReferenceImplementation compile_native_go_fuzzer github.com/transparency-dev/merkle/testonly FuzzConsistencyProofAgainstReferenceImplementation FuzzConsistencyProofAgainstReferenceImplementation golang-github-transparency-dev-merkle-0.0.2/.clusterfuzzlite/project.yaml000066400000000000000000000002131455123105200266600ustar00rootroot00000000000000# https://google.github.io/clusterfuzzlite//build-integration/go-lang/ language: go fuzzing_engines: - libfuzzer sanitizers: - address golang-github-transparency-dev-merkle-0.0.2/.github/000077500000000000000000000000001455123105200223365ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/.github/dependabot.yml000066400000000000000000000003321455123105200251640ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: "/" schedule: interval: weekly open-pull-requests-limit: 10 - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" golang-github-transparency-dev-merkle-0.0.2/.github/workflows/000077500000000000000000000000001455123105200243735ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/.github/workflows/cflite_build.yml000066400000000000000000000013751455123105200275510ustar00rootroot00000000000000name: ClusterFuzzLite continuous builds on: push: branches: - main # Use your actual default branch here. permissions: read-all jobs: Build: runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} cancel-in-progress: true strategy: fail-fast: false matrix: sanitizer: - address # Override this with the sanitizers you want. # - undefined # - memory steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@1e163f06cba7820da5154ac9fe1a32d7fe6f73a3 # v1 with: language: go sanitizer: ${{ matrix.sanitizer }} upload-build: true golang-github-transparency-dev-merkle-0.0.2/.github/workflows/cflite_pr.yml000066400000000000000000000035741455123105200270760ustar00rootroot00000000000000name: ClusterFuzzLite PR fuzzing on: pull_request: paths: - '**' permissions: read-all jobs: PR: runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} cancel-in-progress: true strategy: fail-fast: false matrix: sanitizer: - address # Override this with the sanitizers you want. # - undefined # - memory steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@1e163f06cba7820da5154ac9fe1a32d7fe6f73a3 # v1 with: language: go github-token: ${{ secrets.GITHUB_TOKEN }} sanitizer: ${{ matrix.sanitizer }} # Optional but recommended: used to only run fuzzers that are affected # by the PR. # See later section on "Git repo for storage". # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git # storage-repo-branch: main # Optional. Defaults to "main" # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages". - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@1e163f06cba7820da5154ac9fe1a32d7fe6f73a3 # v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 600 mode: 'code-change' sanitizer: ${{ matrix.sanitizer }} # Optional but recommended: used to download the corpus produced by # batch fuzzing. # See later section on "Git repo for storage". # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git # storage-repo-branch: main # Optional. Defaults to "main" # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages". golang-github-transparency-dev-merkle-0.0.2/.github/workflows/codeql-analysis.yml000066400000000000000000000047641455123105200302210ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ main ] pull_request: # The branches below must be a subset of the branches above branches: [ main ] schedule: - cron: '37 3 * * 1' permissions: contents: read jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # v2.3.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # v2.3.2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # v2.3.2 golang-github-transparency-dev-merkle-0.0.2/.github/workflows/go_test.yml000066400000000000000000000011421455123105200265600ustar00rootroot00000000000000on: [push, pull_request] name: Test Go permissions: contents: read jobs: test: strategy: matrix: go-version: [1.19.x, 1.20.x] os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: go-version: ${{ matrix.go-version }} - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.1.0 - run: go test -v -race -covermode=atomic -coverprofile=coverage.out ./... - uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 golang-github-transparency-dev-merkle-0.0.2/.github/workflows/golangci-lint.yml000066400000000000000000000013451455123105200276500ustar00rootroot00000000000000name: golangci-lint on: push: pull_request: permissions: contents: read # Optional: allow read access to pull request. Use with `only-new-issues` option. # pull-requests: read jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: go-version: 1.17 - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.1.0 - name: golangci-lint uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # v3.4.0 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: v1.50.1 golang-github-transparency-dev-merkle-0.0.2/.github/workflows/scorecard.yml000066400000000000000000000056531455123105200270740ustar00rootroot00000000000000# This workflow uses actions that are not certified by GitHub. They are provided # by a third-party and are governed by separate terms of service, privacy # policy, and support documentation. name: Scorecard supply-chain security on: # For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection branch_protection_rule: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: - cron: '15 21 * * 3' push: branches: [ "main" ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write # Uncomment the permissions below if installing in a private repository. # contents: read # actions: read steps: - name: "Checkout code" uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.1.0 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3 with: results_file: results.sarif results_format: sarif # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if: # - you want to enable the Branch-Protection check on a *public* repository, or # - you are installing Scorecards on a *private* repository # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} # Public repositories: # - Publish results to OpenSSF REST API for easy access by consumers # - Allows the repository to include the Scorecard badge. # - See https://github.com/ossf/scorecard-action#publishing-results. # For private repositories: # - `publish_results` will always be set to `false`, regardless # of the value entered here. publish_results: true # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: SARIF file path: results.sarif retention-days: 5 # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@f3feb00acb00f31a6f60280e6ace9ca31d91c76a # v2.1.27 with: sarif_file: results.sarif golang-github-transparency-dev-merkle-0.0.2/.golangci.yaml000066400000000000000000000003321455123105200235210ustar00rootroot00000000000000run: # timeout for analysis, e.g. 30s, 5m, default is 1m deadline: 90s linters-settings: depguard: list-type: blacklist packages: - golang.org/x/net/context - github.com/gogo/protobuf/proto golang-github-transparency-dev-merkle-0.0.2/CHANGELOG.md000066400000000000000000000001761455123105200226130ustar00rootroot00000000000000# MERKLE changelog ## HEAD ## v0.0.2 * Fuzzing support * Dependency updates, notably to go1.19 ## v0.0.1 Initial release golang-github-transparency-dev-merkle-0.0.2/CODEOWNERS000066400000000000000000000000361455123105200223700ustar00rootroot00000000000000* @transparency-dev/core-team golang-github-transparency-dev-merkle-0.0.2/CONTRIBUTING.md000066400000000000000000000046651455123105200232420ustar00rootroot00000000000000# How to contribute # We'd love to accept your patches and contributions to this project. There are a just a few small guidelines you need to follow. ## Contributor License Agreement ## Contributions to any Google project must be accompanied by a Contributor License Agreement. This is not a copyright **assignment**, it simply gives Google permission to use and redistribute your contributions as part of the project. * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA][]. * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA][]. You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. [individual CLA]: https://developers.google.com/open-source/cla/individual [corporate CLA]: https://developers.google.com/open-source/cla/corporate Once your CLA is submitted (or if you already submitted one for another Google project), make a commit adding yourself to the [AUTHORS][] and [CONTRIBUTORS][] files. This commit can be part of your first [pull request][]. [AUTHORS]: AUTHORS [CONTRIBUTORS]: CONTRIBUTORS ## Submitting a patch ## 1. It's generally best to start by opening a new issue describing the bug or feature you're intending to fix. Even if you think it's relatively minor, it's helpful to know what people are working on. Mention in the initial issue that you are planning to work on that bug or feature so that it can be assigned to you. 1. Follow the normal process of [forking][] the project, and setup a new branch to work in. It's important that each group of changes be done in separate branches in order to ensure that a pull request only includes the commits related to that bug or feature. 1. Do your best to have [well-formed commit messages][] for each change. This provides consistency throughout the project, and ensures that commit messages are able to be formatted properly by various git tools. 1. Finally, push the commits to your fork and submit a [pull request][]. [forking]: https://help.github.com/articles/fork-a-repo [well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html [pull request]: https://help.github.com/articles/creating-a-pull-request golang-github-transparency-dev-merkle-0.0.2/LICENSE000066400000000000000000000261361455123105200220130ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-transparency-dev-merkle-0.0.2/README.md000066400000000000000000000020431455123105200222540ustar00rootroot00000000000000# Merkle [![Go Reference](https://pkg.go.dev/badge/github.com/transparency-dev/merkle.svg)](https://pkg.go.dev/github.com/transparency-dev/merkle) [![Go Report Card](https://goreportcard.com/badge/github.com/transparency-dev/merkle)](https://goreportcard.com/report/github.com/transparency-dev/merkle) [![codecov](https://codecov.io/gh/transparency-dev/merkle/branch/main/graph/badge.svg?token=BBCRAMOBY2)](https://codecov.io/gh/transparency-dev/merkle) [![Slack Status](https://img.shields.io/badge/Slack-Chat-blue.svg)](https://gtrillian.slack.com/) ## Overview This repository contains Go code to help create and manipulate Merkle trees, as well as constructing and verifying various types of proof. This is the data structure which is used by projects such as [Trillian](https://github.com/google/trillian) to provide [verifiable logs](https://transparency.dev/verifiable-data-structures/#verifiable-log). ## Support * Mailing list: https://groups.google.com/forum/#!forum/trillian-transparency * Slack: https://gtrillian.slack.com/ (invitation) golang-github-transparency-dev-merkle-0.0.2/cloudbuild.yaml000066400000000000000000000010451455123105200240100ustar00rootroot00000000000000timeout: 300s options: machineType: E2_HIGHCPU_32 volumes: - name: go-modules path: /go env: - GOPROXY=https://proxy.golang.org - PROJECT_ROOT=github.com/transparency-dev/merkle - GOPATH=/go # Cloud Build logs sent to GCS bucket logsBucket: 'gs://trillian-cloudbuild-logs' steps: - id: 'lint' name: "golangci/golangci-lint:v1.51" args: ["golangci-lint", "run", "--timeout", "10m"] - id: 'unit tests' name: 'golang:1.19' args: ['go', 'test', './...'] - id: 'build' name: 'golang:1.19' args: ['go', 'build', './...'] golang-github-transparency-dev-merkle-0.0.2/compact/000077500000000000000000000000001455123105200224245ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/node_fuzz_test.go000066400000000000000000000015741455123105200260240ustar00rootroot00000000000000//go:build go1.18 package compact import ( "testing" ) // Test that RangeNodes returns a slice of nodes with contiguous coverage. // https://github.com/transparency-dev/merkle/blob/main/docs/compact_ranges.md#definition func FuzzRangeNodes(f *testing.F) { for begin := 0; begin <= 10; begin++ { for end := begin; end <= 20; end++ { f.Add(uint64(end), uint64(end)) } } f.Fuzz(func(t *testing.T, begin, end uint64) { if begin > end { return } t.Logf("begin=%d, end=%d", begin, end) nodes := RangeNodes(begin, end, nil) t.Logf("nodes=%v", nodes) // Nodes should be contiguous covering begin to end previousEnd := begin for _, node := range nodes { b, e := node.Coverage() if b != previousEnd { t.Errorf("got=%d, want=%d", b, previousEnd) } previousEnd = e } if previousEnd != end { t.Errorf("got=%d, want=%d", previousEnd, end) } }) } golang-github-transparency-dev-merkle-0.0.2/compact/nodes.go000066400000000000000000000063731455123105200240740ustar00rootroot00000000000000// Copyright 2019 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package compact import "math/bits" // NodeID identifies a node of a Merkle tree. // // The ID consists of a level and index within this level. Levels are numbered // from 0, which corresponds to the tree leaves. Within each level, nodes are // numbered with consecutive indices starting from 0. // // L4: ┌───────0───────┐ ... // L3: ┌───0───┐ ┌───1───┐ ┌─── ... // L2: ┌─0─┐ ┌─1─┐ ┌─2─┐ ┌─3─┐ ┌─4─┐ ... // L1: ┌0┐ ┌1┐ ┌2┐ ┌3┐ ┌4┐ ┌5┐ ┌6┐ ┌7┐ ┌8┐ ┌9┐ ... // L0: 0 1 2 3 4 5 6 7 8 9 ... ... ... ... ... ... // // When the tree is not perfect, the nodes that would complement it to perfect // are called ephemeral. Algorithms that operate with ephemeral nodes still map // them to the same address space. type NodeID struct { Level uint Index uint64 } // NewNodeID returns a NodeID with the passed in node coordinates. func NewNodeID(level uint, index uint64) NodeID { return NodeID{Level: level, Index: index} } // Parent returns the ID of the parent node. func (id NodeID) Parent() NodeID { return NewNodeID(id.Level+1, id.Index>>1) } // Sibling returns the ID of the sibling node. func (id NodeID) Sibling() NodeID { return NewNodeID(id.Level, id.Index^1) } // Coverage returns the [begin, end) range of leaves covered by the node. func (id NodeID) Coverage() (uint64, uint64) { return id.Index << id.Level, (id.Index + 1) << id.Level } // RangeNodes appends the IDs of the nodes that comprise the [begin, end) // compact range to the given slice, and returns the new slice. The caller may // pre-allocate space with the help of the RangeSize function. func RangeNodes(begin, end uint64, ids []NodeID) []NodeID { left, right := Decompose(begin, end) pos := begin // Iterate over perfect subtrees along the left border of the range, ordered // from lower to upper levels. for bit := uint64(0); left != 0; pos, left = pos+bit, left^bit { level := uint(bits.TrailingZeros64(left)) bit = uint64(1) << level ids = append(ids, NewNodeID(level, pos>>level)) } // Iterate over perfect subtrees along the right border of the range, ordered // from upper to lower levels. for bit := uint64(0); right != 0; pos, right = pos+bit, right^bit { level := uint(bits.Len64(right)) - 1 bit = uint64(1) << level ids = append(ids, NewNodeID(level, pos>>level)) } return ids } // RangeSize returns the number of nodes in the [begin, end) compact range. func RangeSize(begin, end uint64) int { left, right := Decompose(begin, end) return bits.OnesCount64(left) + bits.OnesCount64(right) } golang-github-transparency-dev-merkle-0.0.2/compact/nodes_test.go000066400000000000000000000103131455123105200251200ustar00rootroot00000000000000// Copyright 2019 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package compact import ( "fmt" "testing" "github.com/google/go-cmp/cmp" ) func TestRangeNodesAndSize(t *testing.T) { n := func(level uint, index uint64) NodeID { return NewNodeID(level, index) } for _, tc := range []struct { begin uint64 end uint64 want []NodeID }{ // Empty ranges. {end: 0, want: nil}, {begin: 10, end: 10, want: nil}, {begin: 1024, end: 1024, want: nil}, // One entry. {begin: 10, end: 11, want: []NodeID{n(0, 10)}}, {begin: 1024, end: 1025, want: []NodeID{n(0, 1024)}}, {begin: 1025, end: 1026, want: []NodeID{n(0, 1025)}}, // Two entries. {begin: 10, end: 12, want: []NodeID{n(1, 5)}}, {begin: 1024, end: 1026, want: []NodeID{n(1, 512)}}, {begin: 1025, end: 1027, want: []NodeID{n(0, 1025), n(0, 1026)}}, // Only right border. {end: 1, want: []NodeID{n(0, 0)}}, {end: 2, want: []NodeID{n(1, 0)}}, {end: 3, want: []NodeID{n(1, 0), n(0, 2)}}, {end: 4, want: []NodeID{n(2, 0)}}, {end: 5, want: []NodeID{n(2, 0), n(0, 4)}}, {end: 15, want: []NodeID{n(3, 0), n(2, 2), n(1, 6), n(0, 14)}}, {end: 100, want: []NodeID{n(6, 0), n(5, 2), n(2, 24)}}, {end: 513, want: []NodeID{n(9, 0), n(0, 512)}}, {end: uint64(1) << 63, want: []NodeID{n(63, 0)}}, {end: (uint64(1) << 63) + (uint64(1) << 57), want: []NodeID{n(63, 0), n(57, 64)}}, // Only left border. {begin: 0, end: 16, want: []NodeID{n(4, 0)}}, {begin: 1, end: 16, want: []NodeID{n(0, 1), n(1, 1), n(2, 1), n(3, 1)}}, {begin: 2, end: 16, want: []NodeID{n(1, 1), n(2, 1), n(3, 1)}}, {begin: 3, end: 16, want: []NodeID{n(0, 3), n(2, 1), n(3, 1)}}, {begin: 4, end: 16, want: []NodeID{n(2, 1), n(3, 1)}}, {begin: 6, end: 16, want: []NodeID{n(1, 3), n(3, 1)}}, {begin: 8, end: 16, want: []NodeID{n(3, 1)}}, {begin: 11, end: 16, want: []NodeID{n(0, 11), n(2, 3)}}, // Two-sided. {begin: 1, end: 31, want: []NodeID{n(0, 1), n(1, 1), n(2, 1), n(3, 1), n(3, 2), n(2, 6), n(1, 14), n(0, 30)}}, {begin: 1, end: 17, want: []NodeID{n(0, 1), n(1, 1), n(2, 1), n(3, 1), n(0, 16)}}, } { t.Run(fmt.Sprintf("range:%d:%d", tc.begin, tc.end), func(t *testing.T) { got := RangeNodes(tc.begin, tc.end, nil) if diff := cmp.Diff(got, tc.want); diff != "" { t.Fatalf("RangeNodes: diff(-want +got):\n%s", diff) } if got, want := RangeSize(tc.begin, tc.end), len(tc.want); got != want { t.Errorf("RangeSize: got %d, want %d", got, want) } }) } } func TestRangeNodesAppend(t *testing.T) { prefix := []NodeID{NewNodeID(0, 0), NewNodeID(10, 0), NewNodeID(11, 5)} nodes := RangeNodes(123, 456, prefix) if got, min := len(nodes), len(prefix); got < min { t.Fatalf("RangeNodes returned %d IDs, want >= %d", got, min) } got := nodes[:len(prefix)] if diff := cmp.Diff(got, prefix); diff != "" { t.Fatalf("RangeNodes: diff(-prefix +got):\n%s", diff) } } func TestGenRangeNodes(t *testing.T) { const size = uint64(512) for begin := uint64(0); begin <= size; begin++ { for end := begin; end <= size; end++ { got := RangeNodes(begin, end, nil) want := refRangeNodes(NewNodeID(63, 0), begin, end) if diff := cmp.Diff(got, want); diff != "" { t.Fatalf("RangeNodes(%d, %d): diff(-want +got):\n%s", begin, end, diff) } } } } // refRangeNodes returns node IDs that comprise the [begin, end) compact range. // This is a reference implementation for cross-checking. func refRangeNodes(root NodeID, begin, end uint64) []NodeID { b, e := root.Coverage() if end <= b || begin >= e { return nil } if b >= begin && e <= end { return []NodeID{root} } return append( refRangeNodes(NewNodeID(root.Level-1, root.Index*2), begin, end), refRangeNodes(NewNodeID(root.Level-1, root.Index*2+1), begin, end)...) } golang-github-transparency-dev-merkle-0.0.2/compact/range.go000066400000000000000000000232601455123105200240520ustar00rootroot00000000000000// Copyright 2019 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package compact provides compact Merkle tree data structures. package compact import ( "bytes" "errors" "fmt" "math/bits" ) // HashFn computes an internal node's hash using the hashes of its child nodes. type HashFn func(left, right []byte) []byte // VisitFn visits the node with the specified ID and hash. type VisitFn func(id NodeID, hash []byte) // RangeFactory allows creating compact ranges with the specified hash // function, which must not be nil, and must not be changed. type RangeFactory struct { Hash HashFn } // NewRange creates a Range for [begin, end) with the given set of hashes. The // hashes correspond to the roots of the minimal set of perfect sub-trees // covering the [begin, end) leaves range, ordered left to right. func (f *RangeFactory) NewRange(begin, end uint64, hashes [][]byte) (*Range, error) { if end < begin { return nil, fmt.Errorf("invalid range: end=%d, want >= %d", end, begin) } if got, want := len(hashes), RangeSize(begin, end); got != want { return nil, fmt.Errorf("invalid hashes: got %d values, want %d", got, want) } return &Range{f: f, begin: begin, end: end, hashes: hashes}, nil } // NewEmptyRange returns a new Range for an empty [begin, begin) range. The // value of begin defines where the range will start growing from when entries // are appended to it. func (f *RangeFactory) NewEmptyRange(begin uint64) *Range { return &Range{f: f, begin: begin, end: begin} } // Range represents a compact Merkle tree range for leaf indices [begin, end). // // It contains the minimal set of perfect subtrees whose leaves comprise this // range. The structure is efficiently mergeable with other compact ranges that // share one of the endpoints with it. // // For more details, see // https://github.com/transparency-dev/merkle/blob/main/docs/compact_ranges.md. type Range struct { f *RangeFactory begin uint64 end uint64 hashes [][]byte } // Begin returns the first index covered by the range (inclusive). func (r *Range) Begin() uint64 { return r.begin } // End returns the last index covered by the range (exclusive). func (r *Range) End() uint64 { return r.end } // Hashes returns sub-tree hashes corresponding to the minimal set of perfect // sub-trees covering the [begin, end) range, ordered left to right. func (r *Range) Hashes() [][]byte { return r.hashes } // Append extends the compact range by appending the passed in hash to it. It // reports all the added nodes through the visitor function (if non-nil). func (r *Range) Append(hash []byte, visitor VisitFn) error { if visitor != nil { visitor(NewNodeID(0, r.end), hash) } return r.appendImpl(r.end+1, hash, nil, visitor) } // AppendRange extends the compact range by merging in the other compact range // from the right. It uses the tree hasher to calculate hashes of newly created // nodes, and reports them through the visitor function (if non-nil). func (r *Range) AppendRange(other *Range, visitor VisitFn) error { if other.f != r.f { return errors.New("incompatible ranges") } if got, want := other.begin, r.end; got != want { return fmt.Errorf("ranges are disjoint: other.begin=%d, want %d", got, want) } if len(other.hashes) == 0 { // The other range is empty, merging is trivial. return nil } return r.appendImpl(other.end, other.hashes[0], other.hashes[1:], visitor) } // GetRootHash returns the root hash of the Merkle tree represented by this // compact range. Requires the range to start at index 0. If the range is // empty, returns nil. // // If visitor is not nil, it is called with all "ephemeral" nodes (i.e. the // ones rooting imperfect subtrees) along the right border of the tree. func (r *Range) GetRootHash(visitor VisitFn) ([]byte, error) { if r.begin != 0 { return nil, fmt.Errorf("begin=%d, want 0", r.begin) } ln := len(r.hashes) if ln == 0 { return nil, nil } hash := r.hashes[ln-1] // All non-perfect subtree hashes along the right border of the tree // correspond to the parents of all perfect subtree nodes except the lowest // one (therefore the loop skips it). for i, size := ln-2, r.end; i >= 0; i-- { hash = r.f.Hash(r.hashes[i], hash) if visitor != nil { size &= size - 1 // Delete the previous node. level := uint(bits.TrailingZeros64(size)) + 1 // Compute the parent level. index := size >> level // And its horizontal index. visitor(NewNodeID(level, index), hash) } } return hash, nil } // Equal compares two Ranges for equality. func (r *Range) Equal(other *Range) bool { if r.f != other.f || r.begin != other.begin || r.end != other.end { return false } if len(r.hashes) != len(other.hashes) { return false } for i := range r.hashes { if !bytes.Equal(r.hashes[i], other.hashes[i]) { return false } } return true } // appendImpl extends the compact range by merging the [r.end, end) compact // range into it. The other compact range is decomposed into a seed hash and // all the other hashes (possibly none). The method uses the tree hasher to // calculate hashes of newly created nodes, and reports them through the // visitor function (if non-nil). func (r *Range) appendImpl(end uint64, seed []byte, hashes [][]byte, visitor VisitFn) error { // Bits [low, high) of r.end encode the merge path, i.e. the sequence of node // merges that transforms the two compact ranges into one. low, high := getMergePath(r.begin, r.end, end) if high < low { high = low } index := r.end >> low // Now bits [0, high-low) of index encode the merge path. // The number of one bits in index is the number of nodes from the left range // that will be merged, and zero bits correspond to the nodes in the right // range. Below we make sure that both ranges have enough hashes, which can // be false only in case the data is corrupted in some way. ones := bits.OnesCount64(index & (1<<(high-low) - 1)) if ln := len(r.hashes); ln < ones { return fmt.Errorf("corrupted lhs range: got %d hashes, want >= %d", ln, ones) } if ln, zeros := len(hashes), int(high-low)-ones; ln < zeros { return fmt.Errorf("corrupted rhs range: got %d hashes, want >= %d", ln+1, zeros+1) } // Some of the trailing nodes of the left compact range, and some of the // leading nodes of the right range, are sequentially merged with the seed, // according to the mask. All new nodes are reported through the visitor. idx1, idx2 := len(r.hashes), 0 for h := low; h < high; h++ { if index&1 == 0 { seed = r.f.Hash(seed, hashes[idx2]) idx2++ } else { idx1-- seed = r.f.Hash(r.hashes[idx1], seed) } index >>= 1 if visitor != nil { visitor(NewNodeID(h+1, index), seed) } } // All nodes from both ranges that have not been merged are bundled together // with the "merged" seed node. r.hashes = append(append(r.hashes[:idx1], seed), hashes[idx2:]...) r.end = end return nil } // getMergePath returns the merging path between the compact range [begin, mid) // and [mid, end). The path is represented as a range of bits within mid, with // bit indices [low, high). A bit value of 1 on level i of mid means that the // node on this level merges with the corresponding node in the left compact // range, whereas 0 represents merging with the right compact range. If the // path is empty then high <= low. // // The output is not specified if begin <= mid <= end doesn't hold, but the // function never panics. func getMergePath(begin, mid, end uint64) (uint, uint) { low := bits.TrailingZeros64(mid) high := 64 if begin != 0 { high = bits.Len64(mid ^ (begin - 1)) } if high2 := bits.Len64((mid - 1) ^ end); high2 < high { high = high2 } return uint(low), uint(high - 1) } // Decompose splits the [begin, end) range into a minimal number of sub-ranges, // each of which is of the form [m * 2^k, (m+1) * 2^k), i.e. of length 2^k, for // some integers m, k >= 0. // // The sequence of sizes is returned encoded as bitmasks left and right, where: // - a 1 bit in a bitmask denotes a sub-range of the corresponding size 2^k // - left mask bits in LSB-to-MSB order encode the left part of the sequence // - right mask bits in MSB-to-LSB order encode the right part // // The corresponding values of m are not returned (they can be calculated from // begin and the sub-range sizes). // // For example, (begin, end) values of (0b110, 0b11101) would indicate a // sequence of tree sizes: 2,8; 8,4,1. // // The output is not specified if begin > end, but the function never panics. func Decompose(begin, end uint64) (uint64, uint64) { // Special case, as the code below works only if begin != 0, or end < 2^63. if begin == 0 { return 0, end } xbegin := begin - 1 // Find where paths to leaves #begin-1 and #end diverge, and mask the upper // bits away, as only the nodes strictly below this point are in the range. d := bits.Len64(xbegin^end) - 1 mask := uint64(1)<>uint(lvl)) } // Compute leaf hashes. for i := uint64(0); i < size; i++ { nodes[0][i].hash = hashLeaf(leafData(i)) } // Compute internal node hashes. for lvl := 1; lvl < levels; lvl++ { for i := range nodes[lvl] { nodes[lvl][i].hash = factory.Hash(nodes[lvl-1][i*2].hash, nodes[lvl-1][i*2+1].hash) } } return tr, visit } // rootHash returns a canonical hash of the whole (possibly imperfect) tree. func (tr *tree) rootHash() []byte { var hash []byte for _, level := range tr.nodes { if len(level)%2 == 1 { root := level[len(level)-1].hash if hash == nil { hash = root } else { hash = factory.Hash(root, hash) } } } return hash } func (tr *tree) leaf(index uint64) []byte { return tr.nodes[0][index].hash } func (tr *tree) visit(level uint, index uint64, hash []byte) error { if level >= uint(len(tr.nodes)) || index >= uint64(len(tr.nodes[level])) { return errors.New("node does not exist") } tr.nodes[level][index].visits++ if want := tr.nodes[level][index].hash; !bytes.Equal(hash, want) { return fmt.Errorf("hash mismatch: got %08x, want %08x", shorten(hash), shorten(want)) } return nil } // verifyRange checks that the compact range's hashes match the tree. func (tr *tree) verifyRange(t *testing.T, r *compact.Range, wantMatch bool) { t.Helper() pos := r.Begin() if r.End() > tr.size { t.Fatalf("range is too long: %d > %d", r.End(), tr.size) } // Naively build the expected list of hashes comprising the compact range. left, right := compact.Decompose(pos, r.End()) var hashes [][]byte for lvl := uint(0); lvl < 64; lvl++ { if left&(1<>lvl].hash) pos += 1 << lvl } } for lvl := uint(63); lvl < 64; lvl-- { // Overflows on the last iteration. if right&(1<>lvl].hash) pos += 1 << lvl } } if pos != r.End() { t.Fatalf("Decompose: range [%d,%d) is not covered; end=%d", r.Begin(), r.End(), pos) } if match := reflect.DeepEqual(r.Hashes(), hashes); match != wantMatch { t.Errorf("hashes match: %v, expected %v", match, wantMatch) } } // verifyAllVisited checks that all nodes of the tree are visited exactly once. // This is to verify the efficiency property of compact ranges: any merging // process resulting in a single range generates *all* internal nodes, and each // node is generated only once. func (tr *tree) verifyAllVisited(t *testing.T, r *compact.Range) { t.Helper() if r.Begin() != 0 || r.End() != tr.size { t.Errorf("range mismatch: got [%d,%d), want [%d,%d)", r.Begin(), r.End(), 0, tr.size) } for lvl, level := range tr.nodes { for index, node := range level { if got, want := node.visits, 1; got != want { t.Errorf("Node (%d,%d) visited %d times, want %d", lvl, index, got, want) } } } } func TestAppend(t *testing.T) { var sizes []uint64 for size := uint64(0); size <= 256; size++ { sizes = append(sizes, size) } sizes = append(sizes, 555, 1040, 5431) for _, size := range sizes { t.Run(fmt.Sprintf("size:%d", size), func(t *testing.T) { tree, visit := newTree(t, size) cr := factory.NewEmptyRange(0) tree.verifyRange(t, cr, true) for i := uint64(0); i < size; i++ { if err := cr.Append(tree.leaf(i), visit); err != nil { t.Errorf("Append()=%v", err) } tree.verifyRange(t, cr, true) } tree.verifyAllVisited(t, cr) }) } } func TestGoldenRanges(t *testing.T) { inputs := testonly.LeafInputs() roots := testonly.RootHashes() hashes := testonly.CompactTrees() for size, ln := 0, len(inputs); size <= ln; size++ { t.Run(fmt.Sprintf("size:%d", size), func(t *testing.T) { cr := factory.NewEmptyRange(0) for i := 0; i < size; i++ { if err := cr.Append(hashLeaf(inputs[i]), nil); err != nil { t.Fatalf("Append: %v", err) } } hash, err := cr.GetRootHash(nil) if err != nil { t.Fatalf("GetRootHash: %v", err) } if size == 0 { if hash != nil { t.Errorf("Expected nil hash, got %x", hash) } hash = rfc6962.DefaultHasher.EmptyRoot() } if want := roots[size]; !bytes.Equal(hash, want) { t.Errorf("root hash mismatch: got %x, want %x", hash, want) } if diff := cmp.Diff(cr.Hashes(), hashes[size]); diff != "" { t.Errorf("hashes mismatch:\n%v", diff) } }) } } // Merge down from [339,340) to [0,340) by prepending single entries. func TestMergeBackwards(t *testing.T) { const numNodes = uint64(340) tree, visit := newTree(t, numNodes) rng := factory.NewEmptyRange(numNodes) tree.verifyRange(t, rng, true) for i := numNodes; i > 0; i-- { prepend := factory.NewEmptyRange(i - 1) tree.verifyRange(t, prepend, true) if err := prepend.Append(tree.leaf(i-1), visit); err != nil { t.Errorf("Append()=%v", err) } tree.verifyRange(t, prepend, true) if err := prepend.AppendRange(rng, visit); err != nil { t.Fatalf("AppendRange: %v", err) } rng = prepend tree.verifyRange(t, rng, true) } tree.verifyAllVisited(t, rng) } // Build ranges [0, 13), [13, 26), ... [208,220) by appending single entries to // each. Then append those ranges one by one to [0,0), to get [0,220). func TestMergeInBatches(t *testing.T) { const numNodes = uint64(220) const batch = uint64(13) tree, visit := newTree(t, numNodes) batches := make([]*compact.Range, 0) // Merge all the nodes within the batches. for i := uint64(0); i < numNodes; i += batch { rng := factory.NewEmptyRange(i) tree.verifyRange(t, rng, true) for node := i; node < i+batch && node < numNodes; node++ { if err := rng.Append(tree.leaf(node), visit); err != nil { t.Fatalf("Append: %v", err) } tree.verifyRange(t, rng, true) } batches = append(batches, rng) } total := factory.NewEmptyRange(0) // Merge the batches. for _, batch := range batches { if err := total.AppendRange(batch, visit); err != nil { t.Fatalf("AppendRange: %v", err) } tree.verifyRange(t, total, true) } tree.verifyAllVisited(t, total) } // Build many trees of random size by randomly merging their sub-ranges. func TestMergeRandomly(t *testing.T) { for seed := int64(1); seed < 100; seed++ { t.Run(fmt.Sprintf("seed:%d", seed), func(t *testing.T) { rnd := rand.New(rand.NewSource(seed)) numNodes := rand.Uint64() % 500 t.Logf("Tree size: %d", numNodes) tree, visit := newTree(t, numNodes) var mergeAll func(begin, end uint64) *compact.Range // Enable recursion. mergeAll = func(begin, end uint64) *compact.Range { rng := factory.NewEmptyRange(begin) if begin+1 == end { if err := rng.Append(tree.leaf(begin), visit); err != nil { t.Fatalf("Append(%d): %v", begin, err) } } else if begin < end { mid := begin + uint64(rnd.Int63n(int64(end-begin))) if err := rng.AppendRange(mergeAll(begin, mid), visit); err != nil { t.Fatalf("AppendRange(%d,%d): %v", begin, mid, err) } if err := rng.AppendRange(mergeAll(mid, end), visit); err != nil { t.Fatalf("AppendRange(%d,%d): %v", mid, end, err) } } tree.verifyRange(t, rng, true) return rng } rng := mergeAll(0, numNodes) tree.verifyAllVisited(t, rng) }) } } func TestNewRange(t *testing.T) { const numNodes = uint64(123) tree, visit := newTree(t, numNodes) rng := factory.NewEmptyRange(0) for i := uint64(0); i < numNodes; i++ { if err := rng.Append(tree.leaf(i), visit); err != nil { t.Errorf("Append()=%v", err) } } if _, err := factory.NewRange(10, 5, nil); err == nil { t.Error("NewRange succeeded unexpectedly") } rng1, err := factory.NewRange(rng.Begin(), rng.End(), rng.Hashes()) if err != nil { t.Fatalf("NewRange: %v", err) } tree.verifyRange(t, rng1, true) // The number of hashes is incorrect. _, err = factory.NewRange(rng.Begin(), rng.End(), append(rng.Hashes(), nil)) if err == nil { t.Error("NewRange succeeded unexpectedly") } // The number of hashes does not correspond to the range. _, err = factory.NewRange(rng.Begin(), rng.End()-1, rng.Hashes()) if err == nil { t.Error("NewRange succeeded unexpectedly") } rng.Hashes()[0][0] ^= 1 // Corrupt the original hashes. rng1, err = factory.NewRange(rng.Begin(), rng.End(), rng.Hashes()) if err != nil { t.Fatalf("NewRange: %v", err) } tree.verifyRange(t, rng1, false) } func TestNewRangeWithStorage(t *testing.T) { const numNodes = uint64(777) tree, _ := newTree(t, numNodes) root := tree.rootHash() nodes := make(map[compact.NodeID][]byte) getHashes := func(ids []compact.NodeID) [][]byte { hashes := make([][]byte, len(ids)) for i, id := range ids { hashes[i] = nodes[id] } return hashes } cr := factory.NewEmptyRange(0) for i := uint64(0); i < numNodes; i++ { nodes[compact.NewNodeID(0, i)] = tree.leaf(i) if err := cr.Append(tree.leaf(i), func(id compact.NodeID, hash []byte) { nodes[id] = hash }); err != nil { t.Fatalf("%d: Append: %v", i, err) } hashes := getHashes(compact.RangeNodes(0, i+1, nil)) var err error if cr, err = factory.NewRange(0, i+1, hashes); err != nil { t.Fatalf("%d: NewRange: %v", i+1, err) } } got, err := cr.GetRootHash(nil) if err != nil { t.Fatalf("GetRootHash: %v", err) } if !bytes.Equal(got, root) { t.Fatalf("Got root hash %x, want %x", got, root) } } func TestGetRootHash(t *testing.T) { for size := uint64(0); size < 16; size++ { t.Run(fmt.Sprintf("size:%d", size), func(t *testing.T) { tree, _ := newTree(t, size) rng := factory.NewEmptyRange(0) for i := uint64(0); i < size; i++ { if err := rng.Append(tree.leaf(i), nil); err != nil { t.Errorf("Append=%v", err) } } root, err := rng.GetRootHash(nil) if err != nil { t.Fatalf("GetRootHash: %v", err) } if want := tree.rootHash(); !bytes.Equal(root, want) { t.Fatalf("GetRootHash: got %08x, want %08x", shorten(root), shorten(want)) } }) } // Should accept only [0, N) ranges. rng := factory.NewEmptyRange(10) if _, err := rng.GetRootHash(nil); err == nil { t.Error("GetRootHash succeeded unexpectedly") } } func TestGetRootHashGolden(t *testing.T) { type node struct { level uint index uint64 hash string } for _, tc := range []struct { size int wantRoot string wantNodes []node }{ {size: 0, wantRoot: "", wantNodes: []node{}}, // TODO(pavelkalinnikov): Use hasher.EmptyRoot(). { size: 10, wantRoot: "VjWMPSYNtCuCNlF/RLnQy6HcwSk6CIipfxm+hettA+4=", wantNodes: []node{{4, 0, "VjWMPSYNtCuCNlF/RLnQy6HcwSk6CIipfxm+hettA+4="}}, }, {size: 15, wantRoot: "j4SulYmocFuxdeyp12xXCIgK6PekBcxzAIj4zbQzNEI="}, {size: 16, wantRoot: "c+4Uc6BCMOZf/v3NZK1kqTUJe+bBoFtOhP+P3SayKRE=", wantNodes: []node{}}, { size: 100, wantRoot: "dUh9hYH88p0CMoHkdr1wC2szbhcLAXOejWpINIooKUY=", wantNodes: []node{ {6, 1, "/K5I3bQ6Wz/beVi9IFKizZ073WqI8kGqstdkbmMcTXI="}, {7, 0, "dUh9hYH88p0CMoHkdr1wC2szbhcLAXOejWpINIooKUY="}, }, }, { size: 255, wantRoot: "SmdsuKUqiod3RX2jyF2M6JnbdE4QuTwwipfAowI4/i0=", wantNodes: []node{ {2, 63, "EphrHrAU2E+H65CW1o2SwiJVA1dNragVhsMsOkyBdZ4="}, {3, 31, "fwen9eGNKOdGYC7L1GSwMKBlyjIIZBlsKVkmPGtsZEY="}, {4, 15, "Iq5blg5fdl93qbEUzBBEiGMoP7zyzbwf14JuB5YBidM="}, {5, 7, "D6s+gn79wNsgmdvBv0fVIYCougsU+PUSdtLGrWGmyO4="}, {6, 3, "swSuozoE2E7iTV9cnNGcnjbLEeDq+5ep2hRJuI0pTtI="}, {7, 1, "xv1RcZ3JpQusUjlsGQzsV9kWuITo3aLNpEsKymbFhak="}, {8, 0, "SmdsuKUqiod3RX2jyF2M6JnbdE4QuTwwipfAowI4/i0="}, }, }, {size: 256, wantRoot: "qFI0t/tZ1MdOYgyPpPzHFiZVw86koScXy9q3FU5casA=", wantNodes: []node{}}, { size: 1000, wantRoot: "RXrgb8xHd55Y48FbfotJwCbV82Kx22LZfEbmBGAvwlQ=", wantNodes: []node{ {6, 15, "CBbiN/le+CpZNxEmCVIgfQSl/ZTapYxUOsdKTkiVjtc="}, {7, 7, "npfCeOdllUJZLLRbvEkxlwY7enS6pRlChKVTJjHcevI="}, {8, 3, "5MVDHIWhLErkcLgceSnxZWOTG04QlhIkm3aUEOQLpWw="}, {9, 1, "6EoN2SheMl5oA3qymXw1Ltcp1ku/INU+rBqEe2+jIjI="}, {10, 0, "RXrgb8xHd55Y48FbfotJwCbV82Kx22LZfEbmBGAvwlQ="}, }, }, {size: 4095, wantRoot: "cWRFdQhPcjn9WyBXE/r1f04ejxIm5lvg40DEpRBVS0w="}, {size: 4096, wantRoot: "6uU/phfHg1n/GksYT6TO9aN8EauMCCJRl3dIK0HDs2M=", wantNodes: []node{}}, {size: 10000, wantRoot: "VZcav65F9haHVRk3wre2axFoBXRNeUh/1d9d5FQfxIg="}, {size: 65535, wantRoot: "iPuVYJhP6SEE4gUFp8qbafd2rYv9YTCDYqAxCj8HdLM="}, } { t.Run(fmt.Sprintf("size:%v", tc.size), func(t *testing.T) { rng := factory.NewEmptyRange(0) for i := 0; i < tc.size; i++ { data := []byte{byte(i & 0xff), byte((i >> 8) & 0xff)} hash := hashLeaf(data) if err := rng.Append(hash, nil); err != nil { t.Fatalf("Append(%d): %v", i, err) } } visited := make([]node, 0, len(tc.wantNodes)) hash, err := rng.GetRootHash(func(id compact.NodeID, hash []byte) { visited = append(visited, node{level: id.Level, index: id.Index, hash: base64.StdEncoding.EncodeToString(hash)}) }) if err != nil { t.Fatalf("GetRootHash: %v", err) } if got, want := base64.StdEncoding.EncodeToString(hash), tc.wantRoot; got != want { t.Errorf("root hash mismatch: got %q, want %q", got, want) } if tc.wantNodes != nil { if !reflect.DeepEqual(visited, tc.wantNodes) { t.Errorf("visited:\n%v\nwant:\n%v", visited, tc.wantNodes) } } }) } } func TestDecomposeCases(t *testing.T) { for _, tc := range []struct { begin, end uint64 wantL, wantR uint64 }{ {begin: 0, end: 0, wantL: 0x00, wantR: 0x00}, // subtree sizes [],[] {begin: 0, end: 2, wantL: 0x00, wantR: 0x02}, // subtree sizes [], [2] {begin: 0, end: 4, wantL: 0x00, wantR: 0x04}, // subtree sizes [], [4] {begin: 1, end: 3, wantL: 0x01, wantR: 0x01}, // subtree sizes [1], [1] {begin: 3, end: 7, wantL: 0x01, wantR: 0x03}, // subtree sizes [1], [2, 1] {begin: 3, end: 17, wantL: 0x0d, wantR: 0x01}, // subtree sizes [1, 4, 8], [1] {begin: 4, end: 28, wantL: 0x0c, wantR: 0x0c}, // subtree sizes [4, 8], [8, 4] {begin: 8, end: 24, wantL: 0x08, wantR: 0x08}, // subtree sizes [8], [8] {begin: 8, end: 28, wantL: 0x08, wantR: 0x0c}, // subtree sizes [8], [8, 4] {begin: 11, end: 25, wantL: 0x05, wantR: 0x09}, // subtree sizes [1, 4], [8, 1] {begin: 31, end: 45, wantL: 0x01, wantR: 0x0d}, // subtree sizes [1], [8, 4, 1] } { t.Run(fmt.Sprintf("[%d,%d)", tc.begin, tc.end), func(t *testing.T) { gotL, gotR := compact.Decompose(tc.begin, tc.end) if gotL != tc.wantL || gotR != tc.wantR { t.Errorf("Decompose(%d,%d)=0b%b,0b%b, want 0b%b,0b%b", tc.begin, tc.end, gotL, gotR, tc.wantL, tc.wantR) } }) } } func verifyDecompose(begin, end uint64) error { left, right := compact.Decompose(begin, end) // Smoke test the sum of decomposition masks. if left+right != end-begin { return fmt.Errorf("%d+%d != %d-%d", left, right, begin, end) } pos := begin for lvl := uint(0); lvl < 64; lvl++ { if size := uint64(1) << lvl; left&size != 0 { if pos%size != 0 { return fmt.Errorf("left: level %d not aligned", lvl) } pos += size } } for lvl := uint(63); lvl < 64; lvl-- { // Overflows on the last iteration. if size := uint64(1) << lvl; right&size != 0 { if pos%size != 0 { return fmt.Errorf("right: level %d not aligned", lvl) } pos += size } } if pos != end { return fmt.Errorf("decomposition covers up to %d, want %d", pos, end) } return nil } func TestDecompose(t *testing.T) { const n = uint64(100) for i := uint64(0); i <= n; i++ { for j := i; j <= n; j++ { if err := verifyDecompose(i, j); err != nil { t.Fatalf("verifyDecompose(%d,%d): %v", i, j, err) } } } } func TestDecomposePow2(t *testing.T) { for p := 0; p < 64; p++ { t.Run(fmt.Sprintf("2^%d", p), func(t *testing.T) { end := uint64(1) << uint(p) if err := verifyDecompose(0, end); err != nil { t.Fatalf("verifyDecompose(%d,%d): %v", 0, end, err) } end += end - 1 if err := verifyDecompose(0, end); err != nil { t.Fatalf("verifyDecompose(%d,%d): %v", 0, end, err) } }) } } func BenchmarkAppend(b *testing.B) { const size = 1024 for n := 0; n < b.N; n++ { cr := factory.NewEmptyRange(0) for i := 0; i < size; i++ { l := []byte{byte(i & 0xff), byte((i >> 8) & 0xff)} hash := hashLeaf(l) if err := cr.Append(hash, nil); err != nil { b.Fatalf("Append: %v", err) } } if _, err := cr.GetRootHash(nil); err != nil { b.Fatalf("GetRootHash: %v", err) } } } func hashLeaf(data []byte) []byte { return rfc6962.DefaultHasher.HashLeaf(data) } func shorten(hash []byte) []byte { if len(hash) < 4 { return hash } return hash[:4] } golang-github-transparency-dev-merkle-0.0.2/compact/testdata/000077500000000000000000000000001455123105200242355ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/000077500000000000000000000000001455123105200252335ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodes/000077500000000000000000000000001455123105200301375ustar00rootroot0000000000000003167a8f4de6b14a237e918bd96fea8dd45076bd458c8bcf96b09a61df6846ae000066400000000000000000000000441455123105200410540ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(1) uint64(6) 0e0f7b76aefbacfec1b9d14fb1cf6ddfccbe6772ff60f2ba617fb1b7884df19f000066400000000000000000000000461455123105200417740ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(60) uint64(68) 13cdadf28017adae88657be24090473325f4db10ab8fa3f22541b7cb10b683b1000066400000000000000000000000471455123105200406250ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(97) uint64(511) 13dddce380497392cc2a016ad6b881063b6bd99abfd4ffca8ce2781524e43a44000066400000000000000000000000451455123105200410200ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(0) uint64(75) 2d382dbaf5238a4066cd46a22d5e3d37deeefd23087f7b46aa0352efe2de13a1000066400000000000000000000000471455123105200411420ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(63) uint64(110) 2e26d182e06aac76b42a5fa546fa288652501b7a8384489e7d2ebbe43290b8b2000066400000000000000000000000471455123105200405240ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(34) uint64(221) 4502438e2e714833404e371f4ccdffdb472d8ece05d9cd70685d4911671d73a5000066400000000000000000000000451455123105200404550ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(0) uint64(52) 4b5b8deb6dc0a2aabf73efc8f107f9241a5504cc069224dc666bc2d5ab576f58000066400000000000000000000000441455123105200411530ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(3) uint64(3) 629ea6f8a4d39bad9b1d45832a75a9b5d81814269fa9ca20e9f9c77e83d78548000066400000000000000000000000461455123105200406600ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(5) uint64(507) 6365559667f7be67023423270b88fa3cbeb746c81f784f15382a2f66f3a6a151000066400000000000000000000000451455123105200402470ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(71) uint64(0) 74bfce2710370711bdd53cb9450cb8b84b542b6c0a04b6a02e0cbbdb49e6888e000066400000000000000000000000461455123105200407750ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(81) uint64(82) 8196e8e5d77a138305dac92e1dae4e651c5da36949f7f9164e26d07d7372bc28000066400000000000000000000000501455123105200405540ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(179) uint64(255) 88904c8350b849ec832a6b9b5a891c5cbb856afa6d3b9c165ec0c8a011292734000066400000000000000000000000461455123105200405340ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(1) uint64(767) b05a02e2727c643c61be18ef3d80944cceec0e779ee8377dfea4e4e47c2d9d48000066400000000000000000000000461455123105200411310ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(1) uint64(511) b73cd67a4c77db1aaab5159c2eee5dff74a80e26067a27cfb95832eb0efbf060000066400000000000000000000000461455123105200413170ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(1) uint64(311) d92b8ab319960fa968031e6f81ab556df172f913181333c528e98c6ad436578a000066400000000000000000000000461455123105200403330ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(71) uint64(95) e32129a7a793ab12790555c46d5a0fb4accddacf1cab7e9bbabca02d71cbbdb7000066400000000000000000000000441455123105200415540ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(0) uint64(3) e7b6d88b27442e32ccc06572bb36c824c03427ec1d5dd721b485612f3b55b1ec000066400000000000000000000000461455123105200405670ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(13) uint64(79) e8813e9a7cebcf576a05697f95cff9b1f2dfaecf3969ccccec84582165d2747d000066400000000000000000000000471455123105200413250ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/compact/testdata/fuzz/FuzzRangeNodesgo test fuzz v1 uint64(13) uint64(351) golang-github-transparency-dev-merkle-0.0.2/docs/000077500000000000000000000000001455123105200217265ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/docs/README.md000066400000000000000000000007131455123105200232060ustar00rootroot00000000000000Docs ==== This directory contains documentation for the techniques implemented in this repository. Contents: - The [compact ranges](compact_ranges.md) doc describes the structure from the `compact` package, and how it can be used. - The [images](images/) directory contains the images referenced from the docs. Some images have the corresponding source files, e.g. `compact_ranges.graffle` is the OmniGraffle document for the images in `compact_ranges.md`. golang-github-transparency-dev-merkle-0.0.2/docs/compact_ranges.md000066400000000000000000000304221455123105200252360ustar00rootroot00000000000000Compact Ranges ============== This document introduces **compact ranges**, a mental model and technique for reasoning about Merkle trees[^1] and proofs. We present the definition, the properties, and the applications of this technique. [^1]: In this doc, by Merkle trees we mean a subclass known as "history trees" introduced by Crosby and Wallach in "Efficient Data Structures for Tamper-Evident Logging" [paper](https://static.usenix.org/event/sec09/tech/full_papers/crosby.pdf). ## Definition We call a tree node **perfect** if the subtree that it roots is a perfect binary tree. For example, in the picture below nodes `3`, `1.2`, `3.1` and `4.0` are perfect. A perfect node at level `H` has exactly `2^H` leaves in its subtree, and we say that it *covers* these leaves. Correspondingly, we call all the other nodes, such as `3.2` and `5.0`, *ephemeral*. Perfect nodes are immutable, i.e. the corresponding subtree contents and hash don't change when new leaves are appended to the Merkle tree. Ephemeral nodes, on the other hand, are modified each time a new leaf is added, until they become perfect. For a range `[L, R)` of leaves in a Merkle tree, a **compact range** is the minimal set of perfect nodes that cover these, and only these, leaves. For example, in the picture below, the range `[2, 9)` is covered by perfect nodes `[1.1, 2.1, 8]`, and the range `[12, 16)` is covered by a single perfect node `2.3`.

Note that, in the picture above, range `[0, 21)` could be covered by a single node `5.0`. However, we only use the perfect nodes for a compact range, so it rather consists of `[4.0, 2.4, 20]`. The idea is that nodes of the compact range are immutable, regardless of the tree size. For simplicity, when we talk about compact ranges, we can assume that the ephemeral nodes don’t exist. Compact ranges have many useful properties, some of which are elaborated in sections below. The basic property is that the number of nodes in a compact range `[L, R)` is `O(log(R-L))`, or `O(log N)` more generally. A compact range is always unique, and its shape is determined using a few bitwise operations on `L` and `R`. ## Merging Compact Ranges The core property that makes compact ranges widely usable is that they are “mergeable”. Two compact ranges, `[L, M)` and `[M, R)`, can be efficiently merged into an `[L, R)` range. Consider the picture below for an intuitive understanding of how it works.

Given two compact ranges, `[2, 9)` and `[9, 16)`, each represented by a set of node hashes (three green and three cyan nodes correspondingly), we “merge” two sibling nodes by computing their parent’s hash any time they are both present in the set of nodes. This process repeats until there are no siblings in the set. As a result, we get hashes of nodes `[1.1, 2.1, 3.1]` which, as it turns out, represent a compact range of `[2, 16)`. Note that, when merging two compact ranges, the set of “new” nodes (marked in yellow) that are generated as a side effect, forms a partial path towards the root of the tree. It can be proven that this is always the case, which is a convenient property for implementations. Merging two compact ranges can be implemented in `O(log(R-L))`, or more generally `O(log N)` time. This follows from the observation in the paragraph above, and the fact that the size of the resulting `[L, R)` compact range is limited by the same estimate. ## Merkle Tree Proofs A compact range `[L, R)`, if represented by the cryptographic hashes of the corresponding nodes, can be considered a commitment to the contents of the leaves in this range. The ability to merge compact ranges is effectively the ability to merge commitments. ### Root Hash For a Merkle tree with `N` leaves, the compact range `[0, N)` represents a succinct state of the entire tree. Often applications further reduce the size of this state down to a single hash. For example, in a tree with 21 leaves, as in the picutures above, this would be a hash of the ephemeral node `5.0`. We refer to this as the **root hash**. Merkle tree proofs verification uses the root hash (directly or indirectly) as the trust anchor. Usually the root hash is cryptographically signed and committed to by a server. ### Inclusion Proofs Revisited An **inclusion proof** helps a client to verify that, in a Merkle tree of the given state / root hash, a certain leaf position `i` matches the given value. Intuitively, such a proof is the information that the client combines with the leaf hash in order to compute the root hash, which is then compared with the trusted one. The root hashes should match iff the leaf hash is correct (except a negligible probability of hash collisions). More specifically, for a leaf at index `i`, the server can give the compact ranges `[0, i)` and `[i+1, N)`, which the client can verify by merging with a middle range `[i, i+1)` formed simply as the hash of this leaf. The result will be a compact range `[0, N)`, which can be compared with the trusted root hash.

In the example above, an inclusion proof for leaf `6` consists of compact ranges `[0, 6)` and `[7, 16)`. Note that the same nodes `[3.1, 2.0, 1.2, 7]` can be viewed as the siblings of the path going from the root to the leaf. This is a classic "vertical" way of thinking about Merkle tree proofs. It is used, for example, in [RFC 6962](https://datatracker.ietf.org/doc/html/rfc6962#section-2.1). ### Arbitrary Inclusion Proofs In the previous section we established that an inclusion proof can be decomposed into two compact ranges at both sides from the leaf in question. Note that these compact ranges represent the "complementary" part of the Merkle tree, i.e. the entire range minus the leaf. We can generalize this observation: an inclusion proof for an **arbitrary subset** of leaves is a commitment to its complementary part of the tree. For example, consider the case when we want to prove the authenticity of values within the range `[6, 13)`, as shown in the picture below.

To do so, the server can provide two compact ranges: `[0, 6)` and `[13, 16)`. The client will then construct the middle compact range locally (based on the leaf hashes of values between `6` and `12` that they know), and merge it with the two boundary compact ranges. Then they compute the root hash from the resulting compact range, and compare it against the trusted root hash. This construction is called a **range inclusion proof**. In a more general case, to prove the inclusion of an arbitrary subset of entries, the server needs to provide a compact range for each of the “gaps” in leaves. The verifier will then construct compact ranges for each contiguous part of the leaves in question, and merge them with all the "gap" compact ranges provided by the prover. It is easy to see that a range inclusion proof takes `O(log N)` hashes of space. The general case, **multi-entry inclusion proof**, is less straightforward: depending on the number of leaves in question, and how close they are to each other, the proof size varies between `O(log N)` to `O(N)`. The multi-entry proof is always optimal though, and thus more efficient than many individual entry inclusion proofs which could cost `O(N log N)`. ### Consistency Proofs A consistency proof (or proof of the append-only property) proves to a client that one tree state is the result of appending some entries to another state.

The definition of the consistency proof already contains a hint on how to model it with compact ranges. Suppose a client knows a compact range of the old tree, like `[0, 6)` in the picture above, and the server wants to prove that another state with 16 entries is an extension of the first 6 entries. The server provides a compact range of the appended entries `[6, 16)`. The client can then merge `[0, 6)` with `[6, 16)`, and compare the resulting compact range `[0, 16)` with the advertised root hash. If the client does not have the compact range of the old tree, it can be provided by the server too. The classic consistency proof [algorithm](https://datatracker.ietf.org/doc/html/rfc6962#section-2.1.2) in RFC 6962 doesn’t assume that the client has a mergeable commitment. So, instead of just compact range `[6, 16)`, it roughly[^2] consists of both the old compact range and the one that covers the appended entries. [^2]: In RFC 6962 the proof size has fewer nodes in most cases, e.g. the perfect nodes on the right border of the tree are replaced by a single ephemeral node. ## Applications In addition to proofs, compact ranges can be used in various ways, such as constructing the Merkle tree in a distributed fashion. ### Distributed Tree Construction Applications that operate large Merkle trees (such as [Certificate Transparency](https://certificate.transparency.dev/)), need to compute and/or store the nodes of the tree, for example, to be able to serve Merkle tree proofs or check the integrity of the whole data structure. It can be expensive or infeasible to do on a single server when the rate of updates or the volume of data is high. Since the Merkle tree is a highly regular recursive structure, its geometry can be split into pieces of a controllable size that a single server can handle. See the picture below for an intuition how.

The construction work is split across a number of "leaf" workers, each piece of work is based on a relatively small range of leaves. When the corresponding part of the tree is processed, the worker sends its compact range up to the coordinator. The coordinator merges the received compact ranges in order to construct the remaining top part of the tree. The number of tree nodes decreases exponentially from level to level. This is key to tuning performance of the algorithm. If it is requierd to handle a rate `O(T)` of processing leaves, it corresponds to only a rate of `O(T / 2^H)` at level `H`. By picking a reasonable batch size `B` for "leaf" workers, we allow the coordinator to receieve a rate of `O(T / B)` messages (containing just the compact ranges). A batch size of around `O(sqrt(T))` entries can help leveling the load on the coordinator with the load on "leaf" workers. The described approach can be used to implement a scalable tamper-evident log based on Merkle trees. ### Updatable Proofs Logs based on Merkle trees are rarely static - usually they grow in append-only fashion. Proofs built for one state of the tree get outdated with later states. When proofs are represented using compact ranges, they can be updated by merging in the compact range between the previous and the next tree size. For example, an inclusion proof for range `[L, R)` in a tree of size `N` can be represented with a pair of compact ranges `[0, L)` and `[R, N)`, as described in the section on [arbitrary inclusion proofs](#arbitrary-inclusion-proofs). When the tree moves to a state of size `N+delta`, it is possible to update this inclusion proof by merging the compact range `[R, N)` with `[N, N+delta)`. Updating a proof incrementally is more efficient in terms of space / bandwidth than fetching an entire proof each time. For example, consistency proofs in a tree growing to size `N` take `O(log N)` space, and fetching them `O(N)` times takes `O(N log N)` in total. Incremental updates with compact ranges will total up as `O(N)` since each of the tree nodes is used in at most one compact range in the series. ### Witness A witness is an entity that watches the state of the log and attests to its consistency. As described in the [Root Hash](#root-hash) section above, the tree state can be represented, for example, as a single root hash, or a compact range `[0, N)`. The benefit of storing a compact range is in the ability to incrementally update it as the tree state evolves. See the picture below.

Similarly to the [updatable proofs](#updatable-proofs), the accumulated space / bandwidth complexity of updating the state `O(N)` times is `O(N)`, as opposed to `O(N log N)` if the witness only stores the single root hash. golang-github-transparency-dev-merkle-0.0.2/docs/data_model.md000066400000000000000000000036421455123105200243460ustar00rootroot00000000000000Data Model ========== This document establishes terminology shared throughout this repositotry around the structure and components of the Merkle tree. ### Merkle tree In this repository, by Merkle trees we mean a slightly modified version of "history trees" introduced by Crosby and Wallach in the *Efficient Data Structures for Tamper-Evident Logging* [paper](https://static.usenix.org/event/sec09/tech/full_papers/crosby.pdf). ![data_model](images/data_model.svg) The basis of the data structure is an append-only **log** consisting of log **entries**, numbered with consecutive integers starting from 0. Built on top of the log entries, there is a highly regular structure of nodes of a perfect binary tree-like shape. Nodes are addressed as `(level, index)` pairs. Nodes at level 0 are called **leaf nodes**, and correspond directly to the log entries. Nodes at higher levels are defined recursively based on nodes of the lower levels. Specifically, `(level, index)` depends directly on nodes `(level-1, index*2)` and `(level-1, index*2 + 1)`, and, recursively, nodes of its entire subtree. The data structure evolves dynamically. Initially, the log is empty, and all the nodes of the tree have no data. When new entries are appended to the log, nodes that recursively depend on these entries are updated. While the nodes can be updated, they are in **ephemeral** state. Eventually, when the log grows past a certain size, a node becomes **perfect**, and is never modified again. Effectively, perfect nodes are immutable / write-once registers, and ephemeral nodes are mutable. ### Tree state To represent the state of the entire log, often a single ephemeral node is used which covers all the corresponding leaves. For example, in a tree with 21 leaves, as in the picture above, this would be the ephemeral node 5.0. ### TODO: Things to cover: - Append-only. - Merkle tree hasher. - Structured objects, like proofs and compact ranges. golang-github-transparency-dev-merkle-0.0.2/docs/images/000077500000000000000000000000001455123105200231735ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/docs/images/compact_ranges.graffle000066400000000000000000001676371455123105200275340ustar00rootroot00000000000000‹ėũY—ĸZļ¸_ŸúųžË_ėŌ7įT˙X4"6(((ŽŧADDQlPÔõŨ_P"3ˆšC cēĢŌ&&‹ĖõŦÉl˙ü˙6“đÛÚ[,ƒhú¯˙Æŋc˙ũ͛ēQ?˜ú˙úoŗUü“ûī˙ī˙ūņĪ˙ŸT[vCū6 ƒeü­a UUüöß h6 ŊBAjIßUĩŲú–ļQ(ČÚûīaĪū§PH’äģ“I}wŖI&¸,4ŅĖ[ÄÛjÚØŸéßûq˙ŋĶŨZԝô×~āÆ˙÷˙úįØÛū_ļģĀuâôīÖAėŸ…ė÷ôĪÎbádūëŸËx‘ö˙˙ŌŨ}&ĶĀ_DĢŲ÷zúIY8ƒAčą˙,ä"¤qžûNĮü韅û&÷ģŖ0Z¤=Ąˇ<˛×ŧ›˙uî;ąs/”ũ1ũē˙„Ē5åÍŦ7áD9ũŌk͘–Kz Øm+ōŽBČGe„lTĶĶÛÚ.ÛΕņ–Ļ?¤¯ĻiIjIk˜ŠžŊŊ6BōūcļļÅUSL[ü ¯ŧŸO_†M Û~öÉÚßukg'é';뗰Ėļë“F˛—ėeL{žûûž›n8ėļŗŪ ÷]2ģø°›mˇ˙-HÛÜˌmt|ÕÍ>í_HŌīãî}?—ŲoĸڟZá^ËÚ•ū$ ûŲG~ŋßq¯dÅNöąāߟŲk[C7•Đ~ŋãžbœlŌ~ŋ574šzöw)ûģ_ËļëâŗŸČ¤mPûãMé‘íNÂiļSŌũŠR›Š’L×[žßT,˛§XX_Äõô/ėNyŦĘÚē׆ŊizāŠî¯;QûyäéūšûŲõ×F2­ė¸P›'׊šßßëTŲ0kD­5&´ÛVGō{ŽûķW1Ŋ>Áũi¯ŸĻžžÆŊ–ôjíbč¨G7<čøÃũõ˜TRáYCãGí Á$Ŋ^1eĻGÎÃ6÷ũ›Y;aúOŧäĮŨŽ6úĩ›nvÜžS20WŠÖU˛OöˇôlũÚĩ•M<'äīeß|^^8Ÿ?ĮĘáĩڏaÉŪÔ[6QmŠĢZSģíî°ßŪ`ŲvjqũF=bŗîwTß%…ĄM˜~ĩ)ėĖq_|˛Ģ}{ûíN´ųR{ûë÷ŧÍĮ¯TębĩXŪëgwbmģmzÔm m§m‘N›žîõxbĖúJ¸îÂŦ÷Dŗížéĸ¸iĻ} Ũ‰ĩĘúeeã—WŨm:ŽzėtŒũõë”ī´Š×ÆĀ‰ë YķŸ[ŗ…ŠEn•ÕF1Õ?6… ÔĒiîÔŋߎø@g…šhŲČ܏w@xhw~¤ŋ4Ž*ũÛT›uˇš8^6UEÃŨŠũpģüĘHôQ :Rtä!ą†*ŠĒ#IEZ‚ØLĘЅ ’šũvVĒĨH!;"¤`Čõ•’ģĸ‚ÔeŠRŅBåMqŒ*c´đÅũu@U­‘$ šļ‰¤#ÍDD"ų¨n"&IĄÖ°Ņ]" HwI—[Č ŲvĨDQs)Ô|™B-J0ôb YĒĐ֋>jģBĪ/⨓~ĸTQW&ž2ÜīĪ K]Ą‘+ [Ŋd ūX ôŌ TáÎW%ä'bŠ}Øb5Q)4–ÅlģfR6Qˆ‰Ũ¤ŧAĶHĸJÍ|1ō+K´°ÅD¯VQlŠĒ.ĐÚL ^Ģî÷ˇą%U¯ÅhįKzĸ5I]_Û"’’ŋŪFŒ*-ü:ƒ8W’†Ÿė~Ũéē’ę‚Ŧ"=¤ĨÜD†%(5ŲՍ;AMä‰ßŒ„Ē-oPĢ%Ôåô*´î˛í+–šZQąŠ[ļĐv‹ũ¤­]ģ8ķÛ¸āšE\ī„ÂĀVßn ĢdÛiIz’Âąâø]VˆeĒ;ka™^IŊ7ÖfIHÜŽ°]–zŋ!rŠŧũv=.-WxĩDëJDKĩ”ø[QļÕļ?\‰Ēdž~0Ģ”ŠųŖŠØX–ŗíd4ÅVTn%áXėŒËŖd2{Qy—LCq°ŦČz4GXÅBŗšɕ š¯˛íÄĨY!Ņb'&ËĒę/i—ĢŽžB"íW—h­ŠwrõNOš’ÕôdãJ%s?oŽüíTĒĄĄīv’ŽieG’i}DÔ%gŦ% éIŋ.'ÔJë]D˛í¤Ų˛ûlSZQ QįB “Ÿg$Ún,“BCēKtŅ@YŽéļ!ÜÉåĨžmˇ2¤ŽÜ0 y“ūgrFĪH/ž[ŖÉžÛ,ËEyĸ7‡Be$/kMjSÛ_wy[k雔,T­5EųÎ4…Ą>-ĘcĶ6ĢÅ f¤h¨–&XíbĮˇ˛í&›6_ôP;ÕˇIqšŖ Â+>Ų.•D]hA<ĢĐØk&ÅĒ4^ö‚m°ŸW47–D\¨vėxž%‡ÕĄŊj‰ŒX]¸+6āÆUßÛ!…ŽŠ‡Í†ĨÅVĨhá–[nKx×ĩæa›Įû˛E—;TŅ_ˇK¸ŊÅĮNģîīVŨ”~tfáocV/´ÖaG˛Üa¸kwšũžW$˝áĖëž“đÛēÛÛ=6˛|‹įvc3lJ`Ŧ ŲޏöZéDŊcßÅc]išŨz6Úíf×ëOöץí4ģĢá´ŅöZŨģ0jAÛŠof:1qœAqŪTægŨ_´ˆÕÔ)đK‹ØŽ÷ö‹>Œm… {ÃōĘ!XšˇĄ×žR0\1N†SÉw[ŗMØ)%î8ŪÎĻ5ÁÅņ}?W%=Ôļ+™Qßļp*íYwĨ~ģĪj”ÜŽŧچŽL'eĪë3ÆtžˇĪŧÄ`mr-Ä"įwvÁYäŖ% &åģdʍ”Q mĄäWAŒŠ ß%öķmM­4ũuS˛¨:3L'_m†ĻWœSv}8­(8å˛CúNÔá,¨1å5Ų_‡`ĀTēęĸėÄj­š‘R¯m#l3ęZĸ'Ŗ%ĶĐĸ;oŒŊ7“ÚãÖŨ^ĪæeÕOįMŠÖ´1ë˜jšY MĢCwĘŠöĩ§´[ Év‡*k“jĐ-—'{ÎO[Į)/Ŧ Vé-čĝĒ—§ņņ´_ņô2ŗšnˆA@˜H™øDĨXŠzΞåyĨ%Ψ?oLfEgŧaLjæL&ĘÜŠÎÖÄ´7øķb%ÚTÆäܙĖK•ycßΤ˛č;ëŲĸ¸]b\^ô‚UÕa†‹ŗX„–ĨvÂ,Šū˛īl›lU\î‚ũõ[,ô0.oq™mWb_!ŧ^/‰‰€¤ĒÃîJSčæb*ŽF+&î-W+ÆãÔŪvĪųĩŅæG,UYGÍģË3ëBš5i™´ŕIŧ–Úĩ†š)–äŨŌĒnÜŅŪ.0–=iƒ•JkÎįˇÕ]šžœĐÛҍsKbËöjšģÅw­Žģą[ôwGíũŌT_Å2‡õØfƒ¯ ÖjmbŖŒ×ÖV+îxØiZßÅī$ģĢ"ĸŊÛ_wŽ?#ˆõēįkI‘T}Y#ÚäpįE17#™‚¯­Dž2[Ã]ŊÜĸâõČžk,˛íčRc\¨ˇeÚ''á*ĩŠ™iT[cöfX}ÖcV…[_ŗŦڋepŲ`´ˇCâ:Xn—´RČuĒ›BĄRã6ĶŨŦ c|­Š›ŽĮOHâŽāUî„)5oŒ÷Įwį̰˜Ū‘N(líBsƝÖtĩī ŊAaosüŗđĶ]ŗ÷å,gŽë=pæä"Ūs<ãqŧĪņ}†ôHŪãéá8ΤrYëõxÜ{äm*Üģ‰Ūî/ËJ1q•d_đĖ_t'#!A%„0”ZŪū/QQ7åà §n zëÔÍŅsŅĐ- ËípnųUeŒš ķ"Ë÷˙’ß ōķžˇ‘ũ#„?ũGíũŌč‘ŋČöû‡ēÉO˙Đū{ũô ũôš÷>Ĩƒõ÷mø61î}Aƒũ÷åŊfßĪ\æŪķķûãûķĒĄ„;75ŌŧϰįL-ųŊû˙÷m'`ŨÎ;8´ĩ[ ˇNģõEAČŽQzĪ]îMjĢLjļ5I&Û=öąp¸œß—ĘíŨ#˙Š(‘Ivų:_(Üąü6ōŧŨ”§÷šH\„ÚŦ¤ˇ‡H÷Wé éĒ# ŨÉũũüū~XėNŌ{ÚFeš~Ö˛Ë-ķŲĪŗžKØ{ũŦŨˇĻo2ŋC‘\ Ü6ģöRU•øq#íS- Dæ×‘Čüŧ$ õ¯ŋ0F1'YÚsôŲįŽCbƒžË˜ËõY—sHÖÁšŖcôŠxáíĪ’˙ÜĶũ~ į˙Ĉ?qüŽ˙AũÆ|ģÃŌ×CōĪFĸÅĶíÎÚ ŋUœ0˜Nƒq´~ēY1ŊÖvölŋƒĐ‰ŸĘ* g6”"w5ņĻņS/y0=ß[üÎüŗp˙ų~ģUĐ÷–ÕČ{ũ§ûŅęĪö˛—ļ‚eĐ Ÿu˖›OåՉã{b´J÷šxŪ›gŠĶņŠÖŸwĻæøS/ū+ōË´ÍĄ—nôؕ_ø)õƒAj8~Å âO ˙įŋø˙āô˙Đôņ+~hČû˗ŧ­Ŋė –œi?ôŪ|hZ{o?õEāĶ7‹7Ōk(¤<^žA6ũĢĶAôSôįÔˇīgSˆâ8šÔœEڃŸ#÷W@偎G”ü×_(ėņ¸ũŒ9ėĄ-‚]4u´ßÁt)_ڑõ3Í?˛#Åģ~МbĮŧŽvÕR„(]ĐÕĻCæņØy(sôFía'|Ÿ žė=ß(9žöTúҘ˙ÕõŖãšä…k/N-ügėiõH'^qœxpŏ1Ļ”Î~/ŸĨGČ|4%=ŅØcÍ?Ūø×ļ,ūlÛũ]Tä§fŒyšS^¸ŋ×xBžz÷ėb>ÚîņåųyŠkuKn՟]ŌÃĻŗ(8šáŋIŠM>ö?ĪÕĄđXĨūrŸĒĒö;}ÂIėĩN=°ģŽ^ÃFļ“ĮŖđ҉~õ$ŧÚŖ'=86ûҁA†¯ŪįĶãéųņØh|>ĮČ^žMôĮöRõ|ĮŨ>ŲÍ#f>šxĪŽdË Â÷ ö—ĮúŖĪü›>ųĀĮn ø}Āß÷éœĀ?u^í˙ęĪđøÔûĪđ7ü+´đ‰s[øÄk>ūg> ĀāĶŋ|îļ€_Ą…ŸÛÂĮ_ŗđq°đ?)đ€Āg~øė˙ -|üÜ>ūš…ƒ…˙Y>|ūˡy¯Ÿš-ā_Ąnûū5ķŦûO {Ü9{šû ØĶˇû+4îĪmÛŋfڃe˙YaĪėöüoĀžē-Ø˙I^ėŗ.ö'NÁkũØ_;ėypãėė7`OŪėé+¤=}nÜ͝ņžāRā“|>ūĀ'n ø8v…ÄßwęŦČ?u^ī@˙ęĄ˙~˙ũŗĸũUû$‘ųøpėP–˙Į7 ûĪŽ1ũä\°¯¸īŋ0œ‚}B“Ú=Ÿ]ŽūŪĶŅģĮ5öÃŋ¨G ŋßÚ˙­­ŋĩõ‘™ņ×Z$ ŋ÷*tÎ>ŌŽŒ†Įk šÔoYeāÅeūžQB°%$Œ%Ÿd”\n.Ą`”Ā(ųŖŋÜ\BÃ(QōIFÉåæF Œ’Ī1J.7•°W;Hđŋ0$đŋ40PøË߈_Já9PxPøķ+üŸy)įAãAã/ ņėĨ4žÁ@ãAã/ ņøÅ Īāpŗ 7ģ×3L ?éVō“ˆß(5Ŗą[ËO篌Tüė)ŠøĢ9Š8$Ĩ~Öü$(9čŋQrFŨØú ט”Jœ=)•x5)•€¤ÔO }ƒÚ3 >ųĩgwkÔŋFSŸ8ģŠOŧję`ęZęCPŸü4ŠŊĩå%ŽŅÖ'Īn듯Úú$ØúŸ–ú°‚PŸügųĒ­O‚­˙iŠ~} >ųz¤nlŨĐ?Šk´õŠŗÛúÔĢļ>ļūgĨ>~} >ųO{¤nmQęm}ęėļ>õĒ­O­˙iŠ~} >ų|¤nn%Ņk´õéŗÛúôĢļ> ļū§Ĩ>øõúäo<÷‘ēĩåDéĢ\@úü+Hŋž„4ØúŸ–úā×ę“ŋņđG ŋ1ę3×hë3gˇõ™Wm}lũĪJ}üú@}ō7JsŠ[+ÍeŽŅÖgÎnë3¯Úú ØúŸ–úä‡<;āOübKõŦ[ë–|–%Ü.ļŧÁÃ0aōY†ÉÅfģâa‚ÁŌo7ŦôäÅæĨĨŋŒŌ_Žô(=(ũE”žēéIPzPúË(ũåHOŌƒŌ_DééˑžĨĨŋŒŌ_Žô (=(ũE”žšéYđėƒg˙ŗ “ËÍ '†aΎ€$ēŋ?‰îŨ9t7V-ƒÄ߲T ĢJĄ#÷ŊzŊS[ŨéĶđjŸ ƒîoĘ Ë/™§„ž>‘ŧrĪ܂—wGAŽõWŸ&Ø÷/—HÂ4q†Lk‚å˛nW5QœîLמlM}HĒ5Ží•†'Ī~ }<ŒVsüŠ÷ļĘė;A˙ņíMeYõ] nĻãĪ; Ę˙l/ˇųķĻÛįŖ“ņŋ,âūÙ.ƒũ?îĖĮ šøáFnä¤"’ųĮá[œļąLįĩt3ėđË,tâA´˜`˙ū1Hgû¸ū`?Ķ }w‡ÎbéÅØˇŸĶŊæ­ŧ˙ũĪ?ūn›š+Šđ˙ūXx}‚Ļø Ī›fzáĘKß÷R˙™9Ķž×˙)Ÿūūcæ,ú?â Í`éŋ8Nėß.{#j˙ÆaŲIîEH~/BQ{ۋ¤[go žaØT$k7XLxĩpÂs7û!üaėMŽG ė˙ØŨ’$žũpØ7úûķڟŋîĢŊAü‚§âW‹†—ží`íĄÔ : ė;ūÄc„§ƒú;Nņ_ŸŽx—2Yæ‰(sœ`íQĮúđtˆ€ũ hâũĢQŨؓ…ŪęŅ8ĢũLė;uFëųäYxĩK`:_}",?~õßXiüÆJĶIŠŊēĘôŗģÕOpĒQ§:M|ˆƒ…Ü;X.ā_ÁO$įƒ/|!ā É|!äw|!ā ۘ~˙ēMÄŲÆoõ;œ×ÂĻ"ØŗŲÆ'OÂk=ÛøÚ]!/ģ=÷_÷Ėo¤â€ûĮ=~u¸Į÷_÷/95ˆŊSƒ:ŋSƒ ¯ĖŠ•7SŠî•ŋ×ŊB|§ĀŊōî•ŊĘûĮ7š=;iø üū~ ūBĻßGá—`/„_bzvŸÄûėÄÃy ŪGīR' ÄâņNâ}ņhâoĀāđNī;ØxŗAüc†ņ[ƒø4cAüˇÅËĪ›˛õļ‹ŋ/†âŧÖ!á_{Ÿ†įiė™wÞÁö{ūÚ`Īėŋė_\åįBņ*g83Ā™qʙÁ‚/ãƒŧˇø…âU ÄâņNâ}ņ.¯b`™ītŧ "ô¯æ1ÃøÍ.LęÆV,ŋB&~æxūš ‡xÕįta2°Ø"Ā?ėi€ũ‡Ãžŋ6ØCŧ `°˙œ°įŪŋœ ÃÜė˙Ü?^įę–R?īJę'OÂk=Ü_;îYĀ=āžz?îYĀũ‡ãūO|ßĢĢūÉ>ōų€üĢGūûŸ•ÄÜØÃ’ū¤Ž’ųäŲ™OŊ} ˙YĄO˜č˙FÍ Đ?ôék„> Đčô?)ôŲ÷ûōŲ+4ü“Ø“ ŋ˛Åáßęsúû ú<ŧÚ)€ūÕCŸčyčŋßŖĪâũs@ŸēJęS€ũ¯‡ũ—jū$øŊÖ\āÉ ,ŦL+ĶCQÔ§(ŠĸžÃŌôUúįžĀxŪ4 … P Ė;ũ°i˜÷QĖŖ.=  Đ蝄zPũ.Ö˙ãø÷ģXé[{äōõ=qųė\~åyËđ¸åĪéVå YúËŖž˙h¨˙đPÁ]ëOt`°Ø_=ėÃŽŋĩbȡÖžšōĖŧ?}^í ˙ę‘)Ō_ų8ö>Ė?‡‰…Ė'€ųĀ|`ūgeūoØųˇV I^%ķÉs3Ÿ|ų$0˙“2Ÿæyæãīˇķ9 ˜ŽĒ˜+d>Ėæķ?+ķßoįsˇV I_%ķés3Ÿ~ų40˙“2ŸæyæŋaįĀü30ŸšBæ3Āü/Įü—j˛4¯ ”Ŋsä-•ŊãĄČ˙K%í”°CeĶßXŲD|'Ą˛éŖĘ9ņ}9į%h åœPÎ Đ; = ÷QĐ#/=úļVNˆž~žÂš ÆSúby—Š`Aiˆw:‚ëMC ŧ™GMã7{3ņ[Ģí"¯r…ĒsG°Č׊‚ÖgõfBq0ûę.柁ųØõ1"X_ų/?eáR,ĘģĀš΍SÎ Xĩæã,sŠĶäōN!ę>yŠ`áPƒÄ❎`Áķ!‚ŪĖŖĻņÛŊ™ˇVƒE]Ŗ7“:w‹z՛IAë“z3q¨ÁæãŋQƒ…3Āü30ģ>æCëë1˙Eß}ąÔ`sœ§œœûaî\úb,¨ÁäōN!ŧš‡ŧKE°xX’–d˜^"8Īg…ā8JZŨov”ˇVŪE_ŖŖ”>wpŒ~ÕQJCpėŗ:JĄŧ ˜˙FyķĪĀ|ėú˜Áą¯ĮüŨ&ĖĨ‚c”w§œ'āÚø(O1sŠāå]€<@Ū)äa€ŧCŪ…‚c”wņ€x§#XĀ<ˆ`7ķ¨iüvo歕w1×čÍdÎÁb^õf2Áú¤ŪLĘģ€ųÄo”w 0˙ ĖĮŽųÁēuæ?ŧ–û&•EĐLãģũ)ŅLA›ž¤{?’¤ē—Ž9Ŗh‘mōāĪ&›ŖˇIéũĐą9ÃY¸ÃôæęņŲqb'˙lO…ĨĶҰš$™HW%™ŠĖėrŠēSmÖ#čîA˛Ŧˇ61œõ'ÖÖ ŦG vYágbúĨZ!E9HFEN—0ēÕ$,ēÛ6wĸEN'm/–UŅ2ļ=‚Į’>R&b’tJZMcÕoo–¸ŽÕĨ 2äaÍ,]÷ Ų 5ŠGāIڟ°75v–IˇĖq_ėČtK'øe,ˇ’ŽRu-N—Įé_Hk×Wøø^âđ7{œ;ąē¤Vމ(Q%ä×DjĶeīûī̚Žôƒ$˛‘čŖNKäŽ2\ēŲŪ›Ļ&ĩŗ^ÂĐ&ŦōAŌP‘ä#]—Æœ!É>BhŖļ”pÕKT!!Nûž=HfŊW‹Ãn;@ĨD„ÆHđŅ&•Ú¤–´,-?ŸĘ^BŲ0kž‹—KēžŊ’|l#¤gßĨVÕęĒxJŊáõDR(ŽÜRË>™û]hãnGeß’ÃėWeCöKÂ^ŠÛoeš˙. ŋÚŒmģ:Ų¯ĘAjz˙Ŋšouŧ˙U?ü:îĩéqö}NJBúŨ˙맏É~U‡Dŋm÷'a˙ŊK8m‹Ėž$ņėWŅXöÛø,ûõîĐvÜmwé'YúÕĪTĪģm-Ę~­íOÂfk˙€ÜČA›i~˙<ųŽu;CėÁų”ĩĩ[ ˇNģõEŲÛą_kéē(¨ŨNyėv ŦjeÚŨ‰î§Z§ŧōŌ“ŅUŦ<šFÍŊv{M!Ōö-ĀļՑ|ôj–w{ ”Ãĸžŗ7õ–MT[jĻķīא#¯ėúŠ%š‘ž€ƒF-7vۊüÚ<ŒûN¯3Ųz#ĶŋēūKCÔ{ĨŌMĪ79LBŅf?ÛūÕfš’ũ{—î^$v„ēDí׎Æaņ Šo…¨_2’zĀ­ûdŸŦNÂĐŪŌ#įh?ŗí^Úâī9ŸÂũh{()Ō{*h#™ÖFv\ ¨*awRÄ{)'˛+|TmŨSxÜ „Ũž‚MĢ%­i`ęąŊŋŌî‚ŊŌîËĮ~¯Å$—æÄšt;å°7ŅBĩ؝Ĩ#b֛¸™ū¯ēJ˜#§Í¯Ô”ŦĮ4øųU‹å´÷ÖļÛĻĶ> íŒN›žĒéHėMŒŲA˛¯„ë^ Ėz'ÆĐtž”õ×}()ÔĮ™-•B„KUˇ§˜‘åd‰žõHZt]Nʖ/ėÅÚĻŊļ5tŸŽwád/"čáôp5É uß&­m˙į˜Ķ´™î/*Ļ— R!C5‘HZæ’z:p‘ĄV’NÅ1ęøÂ)>ęé•\4HQŸˇŠÚ(HRx—M4ņÅĒčhŽ‹+TUQœˆĒÉy›_Ē"-=!ēÔCu„h]Z žŧ/ąH—!‘ĩŧMŖ&(ē<@MS¨$ōĩ|Ąá‹ČLģãģ¨}?u“â uLa€ ŲKaė+rdaŽ+Ô ‰^ĘgmԗB/ ‘78ŋÄd6€„T 1ąėĢk4ōķq¤ëå* uąŖ—§h*‹ž_‘QD‰T Đ<cŋzėËĨˆëU­–"¯§”I–Rҝ Ņ“ęH“ÆåķQÛ×&ˆTĨ_¯ ږ"ŋžBl$m’F ŨĄô4胤€ėTot_1šžĒP4e;16B‰’G¨é_>HÆ~K´šLééUjPEI7]Ąš,Ö}Ģ,XéiJ,2oĶļ‹Ąßž=ŗ¸Ņ;–ā™EŪˇ•tŪQʉMf§-ī§Ū ĶĨ2BN_˜sJ‚z†°Ē)ŧ͸TI\>oãJļŪĮĘ.Mto)°\ ķĄPđU1ņĸ¤ĒšŊäQĄTßÚb%R×ū¨)Ö]õ.ëbĶ,×ũ°qÛzŲĶ' ŅŅË+Ú=ŗ|—D†¸•†>3ÅITÉ­J}n‹sǞŅޏŽUÅdˆģqÕÔã™HĄę­’ŧMίRúš–ĩVņ7’TLj}}[—*n-AģŽÔ¨åZ'éØ$ĩĩļc’Mis”¤>ĨŨ!Ē%TŊŠĶš"Mšú$a)VëŦĪ•Ĩ­ŲĐuۓȍúw;‰ãō#bT–E[×7ÂP.Qúd#Ō˛æęŧ!ˇŌ˙ŒÖĻ$ŗ‹<–ʲį7…:‘Įfŗ+Tdy^k&›”NI-ˇ~Ս&É)ˇüM},sf‹6tĨ(ēfĶ0æEui.ŒVũ Yl KšXҞ-Oh÷Š=Ėĸ[*vģit—ňk$—Ã^ģ¸ŽR+Ŋ/ ŗŪĒČĢ~ã÷Ų]#ČĪŧRálÜ1Ju #\(]šoĻ}ÅםĘpf(S×9HN‡‹ĸ˛JzEaÉ*D­ W[…\4LæĨĸęz›íč YĒĨje`^ÉtûŽ@tKn­Ī Tģ4Fž3dĖŌ‚ō’ŒÁĩJ;jāwf‰åL€,U–ũž(ÚjM÷9QÎī&TĶö›ĘPuŠáŨVĒĄøAe­ÆØH j”JØųŪCą!Ē4.m ­\JÆķmË)ëã°.ZQšë†›m'GåĀMį§T^¸Ftí26žzˇ(ķI$5ũBĨ$GųũQ02+ē=Ģ‹áĸŌÅf˜‰•‘9wÄy¯˛ä(XÉ 1^L›kŖŠĖeŊšYU+ęßbĩĒ)Įũ€XT=y•_̀ŽU#uoŲUu̝ÛÁQåÜua„đš’$3Ię$k†šŪå(b­m¸‘:¯MjÛÉŽjÖlgŒ´BõwIŽĨ/4ÅÄĻŖ–­5ŧÕj—4WÅ ­ôūdĒ‹Q/Ÿ7ĩ­Nڒįjŧ›ĸf¨×ՄÚJãbŨTéÁnÂÕc&—ͰúBeØŨ2ŽKv>ZG Ņåē­í¸Q¯ņę$ŽĖSå7&č.’Xŋą• vë.hܕ‘Š c=5õ’4&ĪôöLXČĨ•>Dw\Áõu_Ē5^įaFéūîl&īŌącXVq2îxFPV瘺0ÖBI3ûŦÁ 9—sXm–‹*ŽûÍļVž›Ķ¤9ęWæBΆœ‰­rMnŪ•k•ņfÛĒÎ44Æ+­ŽĻ‘2ļ&t=Æ8ÔÂî 6Æ }SėëŽ%˛ĻŪ5ĖĸŌ3Ŋnŗf•9sŲoɸ6Č-+fÖâCC´TŪ$p3˛:–•„và 7íšÕ#-Ėčäm†ŋ-ōöĀUÛÆŦëXĒí÷+œOÚkĢgX++§÷åjÖVéhŨ~ĮŠŽzJH/; | áü°Ãjš]'´…Ž]™ųĨذōo—ËölpŠ&ŲtÄF~æģecĖ´MžÛ-‡4aķŨYyÂ(.ßĨĩ)Ķö Nš›ë<Ģ„’ĶÍ¸öLufüœ'â†ÃôbĶéUøXTđ|īŊ^¸’'ôĸˇ4ÖĨ OõøbR!EÅ­ ›úTąÜßA7ÉĘÄMŠģN§NõsK­Zŋ9ćÎ°?ĸ‰ ŲŖrģī’KŌ7<…ĻvĨņÜŗCš&gŠ7ˇXĄ<ŽĖådčl…AŊČëb8đ‹wö”[­0,B_öĐ\•īg™öJØŲjâĪ*"¯jŸË%쉆õzŅ Ú‹ĄĪäķĻõ:ÃŨĒ4Ĩ|9P&ę.ī‚î¤\ˆfã`9¯ÖÔUNÚ °­ŲÔŽ2j)ÚD% Ŗ‰SßE1ĸ]˜ Ģąæų蠕h’uäÜßAëŲMÉI.jęB åėFÂA BN’Ū•¨œué}Œ*.Zęé}K•B ’DTŖ]"u‘ļD¤.å#Nj.ĩÂXÉÔ5SĮMŠ-kŽ¯Ĩüž0ģ'Ë÷ūë^5:r§yūŋūõĶ)ũËiû|Ĩ„SĄ¸įĢ$ŧ(šxƒä#÷ø#ŋõW÷ŊgũŠ“=Ύ‹’eļáÃ-ō]ŲrķŅn:â÷ž†ã{B?÷ô$zˇŽxŪŦ5]įaäåa„á†pļŪâa“b7ĪãŊ‹¸9‹'ŠĪCŊÕČŋ"ĸ9“ŖY‡ûū|;ęÔo,žÄĮū/V[ĩ/9%ņRT#Ũk´Š_Žk i0qbīČe{–'õĢɖŗđŊø˜rāĮ”à nT ĻR°ŒŸO¸gŅ“Ŋ|:: 'ĸéŗMž,ôąß&ÜwLžúÁôØÁôŖøČŅL='ޞ@}՛úē÷;"ōÄ3‚į0‚$˛Ķ“īÛø‹“hŧy›§ãĨžŧiüdËÍ^ĮęS/h/Œ#JPøĮZŏ´šŠ?ÃD~Mu˙ĀĪ‹[AühÔæ×Í&i3ņŸ gēįĀCTd˛æ4˜¯ŧ‡QŋS,:Œ–S89v V° zĄgEAŋâmŸĮ¯Ãøuūųøj 7KxÜpuÚ÷6GvŒŲ1ZÅęVGįžŌ'ļXģôÔÔŌáL߸#ÁqĮū"KŨū™rŠĪŧäw…§ š"H–ā8œdūøögú/ņÅqŽĄ(ŒaYžŲ'€Ķ÷b)šeˆÃŗ„pŒūŽqŋ~åŸ&mÍĻšī@ÚõÁ ôöYŨËīĸ3];Ë#öŦŊGĄâS§éx0˙Ų4ņ<Ėū<4ũ<Č~2Æū<ōũ<ÂūˇíāŲD˙$×úŸĮ’y_Kū9~Sy‹“–ÅS| ÎŌëFŅäú{¸ØR0ņĻ˔x˔~•˙gâŸĨvlq‚Á$ķ”Áéô;K§É§ļSŪ}ü;ömûסũ‡ÍķÃĘGŲ˛úhŲ˛‚Lē[ɤ{w"q[it<™ĨáW•E—õ‰$¨ŗ%Ņ:¯uRčŽ=…Ž&!kúËŗūũ…2ämąžēBÖSgf=õë)`ũ'e=TČëŠwŗžēąōöúXŸõéœŦ?u^ë°ūęYOëŋ<ëéwŗūÖĘßék„=}nÚ͝âžŪRŪÃúÍĀ{æŨŧŋĩŌwģFāī{uVâŸ<¯w ˜íĖg0`ū—gūû—ėgoųWiäãgˇōņ×Í|ėüOĘ|ü:Āü÷?š‹ģ1æWiįgˇķ‰×í|ėüĪĘ|˜˙å™Īŋ›ųü­1˙*í|âėv>ņēO€˙I™ĪƒoįË3ŸĀŪŋ˜í­=—ŧJCŸ<ģĄOžnč“`čVčãũ/}üũĐŋĩGđ’Wié“gˇôÉ×-},ũĪ }úCžÂ›=’Ĩ <ŸíøãŲūĨ{>ˇûŊ§ƒ7_dãŲĶĨaüč’;yëÅom}d^üyI– ŋ÷*sÎ>ÎŪûāˇ7?‹ėØjĪ!.5FH#0F>Įų“`/4H($0H>É a/5Hh$0H>É Á/6•00J`”|–Qrąš„ŊÚQ‚˙…1˙Ĩ€Æ_ūãbķ‘{ęKi<'/Åxŋ„Æ_Šņ˙1iF7’fDžÍOâÆxū“ēĘÜRęėšĨÔëšĨä–~Ö4#("čŋņO‚ŧ5č_en)uöÜRęõÜR rK?)ôq ĘȀúī_”¸ĩ5ŸéĢ4õéŗ›úôëĻ> Ļū§Ĩ>ԑõßŋ(qsĢ?_įōĪį_˙ų @ƒ­˙iŠKÃõßŋ(qkk@3Wië3gˇõ™×m}lũOK}đëõßŋ (qkĢ@3Wië3gˇõ™×m}lũĪJ}üú@ũ÷/ JÜÚ:ĐėUÚúėŲm}öu[Ÿ[˙ĶRüú_žúÔû%nm%hö*m}öėļ>ûē­Ī‚­˙iŠ~} ūûW%om)hî*m}îėļ>÷ē­Ī­˙iŠ~} >ņ~ęßÚZĐÜUÚúÜŲm}îu[Ÿ[˙ŗRŸŋ>P˙ũĨšä­•æōWiëķgˇõų×m}lũOK}üCđ'uŠEwH– %H.°čuŠEwH 44ūO_Œņ×ŧô8:Ë:1Ę3 ķ ķŅyæbœgAįAį/Ŗķã<::™' ]Œķ<č<čü…žžu!§0ĐyĐų‹čË(á/63ĀcŠa”ĀÃ& ŖíoĪh{Ĩ:uk+Đâ$ņĮ7âēÚîoøë}úûŌŲN„×zšlS.[~Áœ8Åķô‰äĪĄ÷tä}\Å._~’`ß_ØNŅ0I||Ö3uŠŊĻYâT—`š¸ö”įßūËO§S…ĀĪį|AĶ?ü…įMŗŊpåĨī{Š˙÷ÃÛ˜ißë˙”O˙1sũņ†f°ô_'öo —Ŋĩã°ė$÷"$ŋĄ¨ŊíEŌ­ŗ7ߋ0l*’ĩ,ĻNŧZ8Ꮪ›ũū0öĻĮŖöėnIß~¸ėũûĪQëå/9)BoŋāĸøÕĸáĨg;X{(ĩ€ŽķûŽ?qáé ūŽSüãW¨#nĨL–y"ĘGX;eÔą><"`;ۙ~Ą8uc ĀžŅ—q^Ķ93Sņ3šÎ§NÂĢ=Ëųę-g\%āO?îolåWžŧžúđsģĶOđĻMo:M~ˆs…Ę´ģ€såDb2øAĀ~ĖB~'Ā~0Œé÷?ô’žĩeRßčs8ĢeŒ§&qÎD“S'áĩi|í~—]ž€û¯€{æũĢbĶ8āū ËŖׯûS]āß0đ_Y@)Uúün š¸2ˇhŧ†Î[Îį¯ĪĪßëų!žSāųų ĪĪK3›…U.1101ĀÄL ŋ31@fäGÍ ÄĨæ â @< Ū âá<÷gļ"ÅEGōy€ŧSČãy†ŧ”xHvŖ ˆwŠxßÁʃL7H}8fŋ9õáÆ*ūۘTvŪˇˇ=ļđoK|8u^ë¤=\}Ú,°§Ū{`˙Ұ§Č+ƒũ‰ėoö¯<`áBū[(Ũox3Nz3Xpf|”˙ŋTˊÁ€yĀ<`Ū)æ1ĀŧcvĄ¨ƒķ€yĀŧ“Q+ÔCÔ ™Įlãˇ:2âÆ vßX{Ū°Ž9puō4ŧŪ'pg^ģ;ķåú|@ū—ˆ]Ŋ­J†äŸáą×G|€ĀāNāsī_”‡šĩ‡ũ‘ûõhŽøÄš—Ŗ?}^ī@˙o]¤8c;Z,ĸ$*ĻįÍëįÍŗøîIƒ…I&÷?Ā„ĄaŌ8äA]ã¤AÁ¤“L_uŌ ß?i076i°W9i0gŸ4Ø×' &Oę^baÍg€ūûka ŽEŸ¯ú@ Đ˙¤Đg#ĻpcŌÉŽmĄ˙ˇúŦū>æŸ: ¯÷ õČĮų_ųŋāųgXáä ‘Īōŋō_~ēKöx— T˛,â‹øCŨԍũFŨõVņ˙°bYr˙❠˲ømM Ø_˜ đŋ~ü#0åĖ>ãĨØ[œÄ)œ%ķą|鋓ũ ˙øí~Ÿäã}”u•ĸĮaĘú¨)‹ŊؔEĀú`§ôNBėtXßœö˙ĮąīvÚŗˇVėK\åŲņŗ?’xũ™ė8<”ũ“ēí9HÃ˙ōĐįߊe)€ū9 ~¯ú@ Đ˙ŦĐ˙ K˙Ö vŠĢ„>yvčS¯CŸčŅ‚]Ōøŋü¤cŋqĢĀĀŦqŽeŽqÖ `րYf/;küÆŊÆ­•ü2W9kĐgŸ5˜×g fĪę`"ú_žúøoÜ+p@ũsŦîsÔg€ú@} ū§ĨūoØúˇVöË]%õŲŗSŸ{ú,P˙ŗRŸęyęīˇõ9 ¨ŽEŨŽ‘úP˙ëQ˙Å )b_!uõ¸›*ęÅ?¨¤÷T…ęmõĻ“Pė-ĻOöķsō{Ē8Pãu5^Äwjŧ>ǰ•ēļ X‹Öblß0ļ ĀöGa›šļIĀ6ØÛ°ˆ,ĸsŖ“,ĸķa“wąI‹‚Et`€ŪIčÁ":=œe˙øF\z4@ Đ;=œæ}ķâRĖc€yĀ<`Ū)æQĀŧc–eÄ\`…XŽæķ€y§˜÷čÁ ąę{Ä6~sĻī­UõŊu-Ö3/ˆeˇMį||ķÉķđz§ Ķ÷ę3}Y¨īøōЧß} ŽeÁé+„ūŠNôoú/ģ7.Æâ1po€{ܧÜPņqĪŠŧT‹ĮyĀ<`Ū)æAaÁĮ1īRa,tĖæc1=cG“ĮßíŅäoîA‡WéŅ$ĪÆ"^÷h’Æú¤Mĸčcī‡>Đ?Gî}…Đ' Œõõ ˙˛{ãba,¨Æ÷¸7Nē7 ėūã–øēX Ēą€yĀŧ“ĖƒĒûcŪÅÂX,ŦF ĢcÁ˛†7ˆƒ… >ŲcÖũ›}˛ˇVOF^ĨO–:{ Ž|Ũ'KA îŗúdĄž  ˙ūz2žčŸ#û‚žBč“ˆûzĐŲAsŠ@\zŗđЀ‡æf=4<øg>Ę­NŖ6Īnˇo˜Ûpûø}Šp(ŽÁcŪā1o@í[އÂĸÂ×øŅ[”ˇúÆqėÖJŠĢtŽĶgˆR¯;Įiˆˆ~Ŧsü—-Ņ Ķķæõsŋ–=ûnįú1ëBĒ_nŪĀ~cŪ `Ū8G& }…ķAU˜7ūŪ¨,}š¨,ÔGB­xYNyYā&æg.Ķ„úH`0īķ`ÁģcŪåâđ´:`0īt4 Ös‡hxEĮo÷ŠŪZ}!}•^QæėŅ4úu¯(Ņ´OZjrĖ6„hؗã>ũÜįûįČĸ ¯û4DÞ ÷_ör\,š…ÃCëĀË^ŽS^XAéÃ<ģėÅĸY8<´˜Ė;Å<¨’ø8æ],š…ÃCë€yĀŧĶŅ,xR'DŗĀĢyÔ8~ŗWŋĩÚ0æ*ŊšėŲŖYĖë^MĸYŸÕ̉Cmp˙Ú.œîŸ#‹žBî3Íú‚ÜŲËqšhÔf—ŧ§ŧąûaž]îrŅ,¨ÍæķN1üēĮŧËEŗXXĢÖØŊ‚‹ąš8‰S8KæÃųŌKiæŖ?č?öēÜī“|ŧO˜ŗŽ2 ™†h$xĨŪÜŧŨ+}kĩuėUzĨšŗG#Ų×ŊŌD#?­Wjë€ûøoÔÖás*ö›ũ)n:z‹<*>?œoö{RA˙ąÕø 7?%šéŲKÛ~°û#įz/]sFŅ"Û䊞<ƒãQ—NzéAĖY¸Ã`ũ„KNėäŸíаt:V“$éĒ$ S’™].uCwĒÍzŨ=H–•âÖ&†ŗūÄÚZĸõHÁ.+|āLŦQŋTk"¤(ɨČéFˇš„EwۆāN´Čé¤íŞ*ZÆļGđØAŌGĘDL’NÉ BĢiŦúíÍ׹ē´A†<Ŧ™EŖë$[Ą&õH*’|¤ëԘ3$ŲGmԖŽz‰Ē#$ÄiߡÉŦ÷jqØm¨´ķ‘ˆĐ >Ú¤RC›Ô’–ĨåįĶBŲK(fÍwņrI÷Ņŗ×A’m„ôėģÔǚC]OIĸ7ŧžH ő[*cŲ'sŋ mÜíhŖėûAr˜ũĒlČ~IØKqû­ŒĀ!÷ßEáW›‚ąmC'ûU9HMīŋWķ­’÷ŋę‡_ĮŊ6=ΞīĪIIHŋãŸũ7Ų¯ęčˇ­ņū$ėŋw §m‘Ų÷ƒ$žũ*Ë~ŸeŋŪڎģíâ.ũ$Kŋú™ęyˇ­EŲ¯ĩũIØl­b9Y#h3ͯŅã_ƒ'ßąngˆ=8Ÿ˛ļvKáÖi÷Ŗžˆ"{;ök­1]ĩÛé/’ŨށU­LģâĄ;ŅũT‹ãTƒW^z2ēŠĩŗ“'רš×Ž`¯)DÚĄØļ:’^Íōn¯rXÔwöĻŪ˛‰jKÍtūũrä•]?õądC3ŌpШåÆn[‘˙B›‡rßéu&[odúW×iˆz/ Tēéų&‡éQ(ÚėgÛŋÚ,W˛īŌŨ‹ÄŽ0B—¨ũÚÕ8,$õ­õKFR¸uŸė“ÕIÚ[zäígļŨK[ü=įS¸m%EzOm$ĶÚȎkĩQe#ėNŠx/åDv…’Ēĸ­{ ģ°ÛS°‰bĩ¤5 L=ļ÷WÚ}@°WÚ}ųØīĩø‘äԜX“n§ö&Z¨ģŗtDĖz7Ķ˙UW ķqä´ų•š’õ˜??ĸjąœöŪÚvÛtÚGĄÁiĶS5‰Ŋ‰1;Hö•pŨ „YīÄzƒÎ—˛ūē%…ú¸ā#ŗŗĨRˆpŠę6â3˛œ,¡ŪI끎ËIŲō…ũąX›Â´×ļ†îĶņ.œėEÄũ’]"œކ ™ĄîÛ¤ĩí˙súƒ6ĶũĨãƒCÅôŌ Bj‚"$b¨æ#IË\RO.’#ÔJŌ‰ 8F_!ÅG=]Øĸ’‹)ęķ6UI &ž8@Íuq…Ē*Š‘C59osãKU¤Ĩ'D—z¨Ž­K ”“÷%é˛ $˛–ˇiÔE—¨i •DŪĸ–/4üb™iwübĩīįŖnR\ĄŽ) "!{)Œ}ÅAŽ,Ėueƒzc!ŅKųŦú˛@čĨ!ōÆį—˜Ėjĸ!&–}uF~>ŽtŊ\EĄ.vôōMeŅķ+2Š(q‚*š'bėWī}šqŊęŖÕRäõ”2ÉR*úĩ!ÚbRi¸|>jûÚ‘Ē4đëDÛRä×Wˆ¤MŌhĄ;”ž€}ęî "&×CŠĻl'ÆF(Qō5ĄâËÉØoɂV“)=ŊJ Ē(éĻ+4—Åēo•+=M‰EæmÚv1ôÛĄg7zĮ<ŗČûļ’Î;J9ąÉė´åũÔģsaēTFČé sNIPĪV5…×Ũ’°—*‰Ëįmb\ÉÖû˜@ŲĨ‰î-–+aū  ž*&ū@”T5ˇ—üĄ#*”ęûA[ŦDęÚ5ÅēĢŪ%c]lšåē6’b[/{ú¤!:zyåOĸg–ī’ȡŌĐgĻ8‰*šUŠĪmqNU6úÂ×ĩǘ,q7ޚz<)T U’ˇÉųUJ_Ķ’ Ö*ūF’ŠI­¯oëRÅ­%hוĩ\ë$›¤˛ÖöqL˛)mŽ“’Ô§´;Dĩ¤€Ē7u:ˇC¤)WŸ$ !Åjõš˛´5ēÎ{5B˙n'q\~DŦ€Ę˛hëúFĘ%JŸlDZÖ\7äVúŸŅÚãƒdv‘įÃRYöüϰQ'ōØlv…Š,ĪkÍd“Ō)ŠåÖ¯ēŅ$9å–ŋŠeÎl҆ŽE×lÆŧ¨.ͅŅĒ$‹ dÉC+Zļå í^ą‡Y”`KÅĀn7î˛qíƒärØk×QjĨ÷…"avÃ[yĩÃoüž"#ģkų™W*œ#FiĸŽa„ Ĩ+wãÍ´¯øēSÎ eę:ÉépQTVI¯(,Y…¨õ‚ájĢ𑋆ÉŧTT]oŗ$KĩT­ Ė+™n߈nÉ­õYj—ÆČs†ŒYZPŪA’1¸ViG ãÎ,ąÜ€ ĨʲßE[­é>'ĘųŨ„júÃ~SĒ.5ŧÛĒS54?¨ŦÕ‰AR ;ß{(6Dĩ€ÆĨ­Ą•KÉxžm9e}ÖE+*wŨpŗíä㨸éüâ”Ę wˆŽ]ÆÆS/đe>‰¤Ļ_¨”ä(ŋ? FfEˇgu1\TēØ #ą22įŽ8īU–ÜKâ Y!ƋismT‘šŦ77ĢjE]â[ŦV5帋Ē'¯ōĢĐĩj¤Žâ-ģĒnõu;¸3ǜģ.Œ^S’d&IŊƒdÍPĶģEŦõĸ 7RįĩIm;ŲUÍZ‚팑V¨ąūî Éĩô…Ļ˜ØtÔ˛5Ŗ†ˇZí’æĒxĄ•ŪŸLUb1ęåķĻļÕI[ō\wSÔ õēšP[i\Ŧ›*=ØM¸ú`Ėä’ŖV_¨ ģ[ÆubÉÎGë¨!ē\ˇĩ7ę5^áÃdÑyJĸüÆŨEë7ļrÁnŨģ2R1aŦ§ĻŪA’Æä™Ūž š´ŌG‚čŽ+¸žîKõąÆëœ #Ė(ŨߝÍä]:v Ë*NÆĪĘJsÆZ(ifŸ58!į’`ĢÍrQÅąqŋŲÖĘssš4GũĘĀ\Čؐ3ąUŽÉÍģr­2Ūl[ՙ†ÆxĨÕÕ4RĻÂքŽĮ‡ZØ=ÁÆXĄoŠ}ŨĩDÖÔģ†YTzĻ×mÖŦ2g.û-ךeÅĖZ|hˆ–Ę›nFVĮ˛’ĐnXáĻ=ˇz¤…ŧÍpāˇEŪXŖjۘukBĩũžc…ķI{mõ keåôžŗ\ÍÚ*­Û/ã8ÕqCO éeg$œvX-ˇë„ļĐą+3ŋ ļSōírŲžmNŅ$›îÂČĪ|ˇlŒ™ļÉwģå&lž;+OÅåģ´6eÚ~Á)wsg•PrēŗמŠÎŒŸķDÜp˜ūĸ@l:Ŋ ‹ žīŊ× Wō„^ô–Æē4áŠ_L*¤¨¸uaSŸ*–;ō;č&Y™¸Iq×éÔŠž``nŠUë7‡ø°ĶöG41!{Tn×á]rIú†§ĐÔŽ4ž{vHĶäLņæ+”â‘Į•šœ ­0¨yŊC ~ņΞ˛â`̆ĨBčËšĢōũ,Ķ^ ;[MüYEäU­ãŗsšd7Ұ^/T{1ô™|ŪtŖ^g¸[•Ļ”/ĘDŨEã]Н” Ņl,į՚ēĘIļ5›ÚUF-E›¨da4qęģˆ#F4Ŗ 3a5Ö#´‡JĶ›UGcŒhÅ3Ũ՚ɗ­AčNRur] “y§ßøyk¯Ęãņ¤+v åY4‰Û=}ļZO…mNÅ ģ#§íf›Ņh:gĨî]uZ˜ûŽ#Ų‘é ãš:É 6sFÂ\#ĸ;glÍ[ĨY+įŽíĖĸÕ¤āôWŗ;1ŋîæ|$Ī[ŪlîDî<ē[ yŒĪī&ËNe§/ĖfŧĒPųˇ˜+ëâüŽēDbŌīIķe[Üā‹rušŦėęÕú*Ûšå?­š­Øž¨×ĨãĩH:Ձŋ*N(ŦVVn…Šŗsü šÚ2ėŒM†ëōŠ/öˆÆÚŸß ĢlaMÎ ŧ+$I}wo+rĨQD‚Ķė„oÉĻÛĒoĖuq[ë*›eŖ¤ģ^á š•Hu]3ÛŪ´R¯ÍÉíŽW— žĢv4Í%đŨ¨—ΰņ’#wėH¯÷EkíŒuŦ°eŠeh —§æ–ˇrIŧ_j[ũžãģĨ ĮD}ÔíiĶ 1í9wü*5ž;÷×(Æt˛ÛķJÛé`q‡ĒJzŠF…€¸Ķō™‹ē[ŨģGÛ­‰äĨ÷:é]ÄÜķˇLu73VS ×휟^œ0…õr¸Â ļģ[Uę ÆbŌz×lŽŪŲ¸ëR~ė\´Û)ƒú”—ZX˛ļtžĪŊAåŠĨ ‚čŽ5Ę-ÕMaÖš[ Ø~!)Ę#ž2 ˜Âx}G6ø¸P(4K[”s^ Ŗqî‰[ŲmĪ Ž’eļáÃ-ō]ŲrķŅn:â÷‰ Į÷„n~îéIČq/\ņŧY+jēÎÃøÜÃ(č!TęlŊÅÃ&ėŸįĨ,â^ä,ž„(Ÿ§¤T#wüŠˆæLŽfHīûķí¨SŋąxÆ˙gŧX=nÕ ŧä”ÄKQt¯Ņ*~9ށĻÁĉŊ#—íYNį¯&[ÎÂ÷âcʁS7X¸Q-˜JÁ2~<ážEOöōétč,œ8ˆĻĪ6yGÛoî;&Oũ`zė`úQ|ähĻžGYBQ՛úbZ÷;"ō`*ÁsAYōʓīÛø‹“hŧy›§ãĨžŧiüdËÍ^ĮęS/h/Œ#JPøĮZŏ´šŠ?ÃD~Mu˙ĀĪ‹[AühÔŪGîĸIÚLüį™Ļøsâ-üĮÅûĖi0_yãø?;Géœu *ĮŽÄ –A/ôŦ(čWŧíŗŖųu0ŋ.Å?_äf)ÚûaŽNûŪæČŽą#;FĢ8BũŅęá¸øÕSúÄË`—žšZ:ƒéw$8îØ_d‰?“$NáY篃l ‚ä¸ī4Nūü÷Įˇ?9"',öķ™%r4O}O˙˙ķĖßHœ'ŋųķäĶ<Ŗi÷Hģ>„Ū>Ųcų]tĻkgyäĀžĩ÷(ųãÔi:žøņl˛xžŒđhåäļØOëŲlõ8Íōį6,Ÿí§^Ú.ŦQß{#ŦÅ(\MĻć-N°˜dž˛8†gétųԆĘģĮžÍâo˙úļ˙°y~Xų8[VÍæ/[Xų{#™ŋėģŲKûĨpüoG_WÚīžW4ɝ/í÷äyxŊSö{íiŋ Õ_žųÜģ™k VRĖU2Ÿ9;ķ™×™Īķ?)ķ`ū—g>˙næßÚb•ôUÚųôŲí|úu;Ÿ;˙“2ŸĮ€ų_ųŋSԍŨô¯ŌЧĪnč͝ú4úŸú8@˙ËC˙7VpÂoí!ŲWié3gˇô™×-},ũĪ }úcÖņ ČL#pîüĢ•˛Wģ‚OAü %M?dUˆŋĒņÜĨ4žŋ€ÆĶc< ŋãO=j44ūC5žšãO=h44ūw5ܕ7âŽ$Éwģ+O<Ôäģ+¯2FŜ=FÅŧŖb FõYŨ•ŒЧŪũ[{Č{•1*öė1*öõ 1ĒĪēØ<éh@}úũÔŋĩGŒ°Więŗg7õŲ×M}LũOK}ČGę3ī§>}k–ēJ[Ÿ;ģ­ĪŊnës`ëZęC‰9P˙ũˊĖ­Q˙*m}îėļ>÷ē­Ī­˙iŠ~} ūû!nm5)ū*m}ūėļ>˙ē­Īƒ­˙YŠƒ_¨˙ūĨEˆ[[OŠŋJ[Ÿ?ģ­Īŋnëķ`ëZęƒ_˙ËSŸz˙â"Ä­­(…cWiėēuVîŸ>oč˙úÉž} ˙ûW!ą›#?sägÎO~æ ä›˙Ķ’üû@~âũäŋĩĩĨŌ›āĢ$?~~›ƒÍƒÍ˙YÉO€Č˙ū2]’¸9ō_§ÍŸßæĮß`ķã`ķZōãŗ´ sŠExH–$%I.°{ŠExH 44ū1ÆĶ ņ ņĐxîbŒg@ãAã/Ąņcüõ.~J3ũŋ$Ŋø #tūœ:Ī_Œō܍čsĐgú}€ūg…ūûkK˜[[+–ŋJčsg‡>˙:ô9€ūg…>Đ˙ōĐ˙#ā –5DŽ‘ú'{Øėö¯ûėoxõom­X6#qmeäou;ũĪ€;u^ī@˙ꥋ‡ôÃĢĪôĪ}ūĄĪôŋô_Ž7Ä2ĨačŗįŦą'RōŗæŽíG nϐĪwpÛEš|öb0ŒĄöŸxî#’ŪnoŦž?ī¨Ũđī‹x€˙pĻË`˙;ķq‚&~¸‘9韆dūqø§m,Ķ8Ũ ;ü2 x-&Øŋ RÃ$î…?؏Á4HßŨĄŗXz1öí§eĸy+ī˙ķ§Ûϊœ ˙ī…×'hú‡ŋđŧiöĄŽŧô}/õ˙~x›™3í{ũŸōéī?fÎĸ˙#ŪĐ –ū‹ãÄūá˛7‚ öo–Ŋ‘ä^„ä÷"ĩĄąŊHēuöÆā{†ME˛vƒÅԉW 'ü1wŗâĀÆŪtā¸q´ĀūņũŅ-IâÛw€}Ŗžc˙9jmũĨÔĮĐÄ/$>ūjŅđŌŗŦ=”ŽŽãŦÄžãØãūĮˇôĮ|`ū|e(ÅžsOdŠL–y"ʇm;Ĩéą><ÕÜßX.*Ã>‰_û8`r›ĻįĻ)ųš~MĶ#šN Ā)XŅ`E÷OrŦčßá>xĖo%;†}ˇĮœ%oĖcūÖ2Ŗ3?‘i¯¨szĖOž‡×;ķk÷˜s˙åĄĪŋ?LĘRũsäÆ\#ôi€>@ ˙YĄ˙–ū­Uŧ2W }æėĐg^‡>Đ˙ŦЇ,ø/}û SŸ柁úė5RŸęõúŸ–úŋaëßZÍ+w•ÔįÎN}îuęs@ũĪJ}¨˙劏˙†­ĪõĪQũtÔįú@} ū§ĨūoØúˇVķúæ•dÎŊĀÍŲšúLŧĄ[@ūĢ'?ä˙ōä'Ūoīs˙,äĮ¯“ü8˙ë‘˙Åb-z_Ģu8(}…Z-¨Õ‚Z­SĩZÄwjĩ>ĒF—ŊöĄD°Ø럯>Ø˙(ėķÃ> Øėöû'ą+ō|܊<øÅ¸OŨ÷áņÍÔĪTXęæÃ€J‘iߊKđ”;ėh°Ŗû'°Ķ@ũŖ>w)ę3@} >P¨ŠúP˙èOg9SX͘cú@} >P˙õŋŗ€}XÍRâÜŊ9#ūÖ*`ßēnđ™W3f˛[įs>ęûäyxŊS õŲđ,ÔA}yčĶī‡>Đ?ôņk„>Đ˙rĐšęRÁ\ú pđ€ƒį¤ƒ* >Ė­O_*˜ËCá+P¨Ô?I}(€ú8ę_*˜ËCŨ+P¨Ô?ĖeûĖŋ>ŋÛ¯ĪßÚŖiéĢôëĶgæŌ¯ûõiæ~Rŋ>.čcī‡>Đ?ôņk„>sŋô_tđ0 æBe.8xĀÁž“XāæÃÜúĖłšP™ ÔęõORVáų8ę_,˜ •š@} >P˙t0–á`.øõŨŊŲ¯k•šĖUúõ™ŗs™×ũú s?Ģ_*súī¯Ėåy€ū9 _#ô!˜ûõ ˙ōSĒ.ĖMo–ĀÃđđ€‡į„‡‡˙·=›ģõĄ4¨ÔęŸĸ>Ô˙0ę_*˜‹cPš ÔęõOGsay}ˆæ‚c˙č Ō[=û8vkÅšėUēöŲŗĮsŲ×]û,Äs?Šk˙˜u Ũ/Į}ė7¸O÷ĪÁ}üš!Ũ/Čũũ<ÜåbēPĄû&?<‰Ü'§ûRõaNsîrĄŌ›*|==˙/I/ū|§€Ķ÷á–‰ü8œ^. Ĩƒ„$P˙t ž1HđEŊķxģ/úÖjJšĢôEsgAr¯ûĸ9ˆA~Z_4•÷1ú7¸Ī÷ĪÁ}üš1Č/Čũũ<üÅb8ԕ‚Ÿü<āį9åįuÃ>ĖģĪ_,XŠC])P¨Ô?E}¨/ú8ę_,Ļ‹_7E2§— –Â#´!X NķŖwovšãˇV°É_Ĩ͜?{°”ŨiÎC°ôŗ:Íq(ØîãŋQ°‰SĀũspŋFîC°ô r˙E Ž].Z ›ā7ŋ98zN9z 'ūÃüæ8všp)x÷?û—‹—B ,`°Ø?ÖÅüÖ÷ūą;¤ˇģ÷o­ĮŽŌŋcgėž>o菸¯ßÅu°Ā~ü7ę`qØöã×É~ī~AöŋėđÁ/Ū% >āđ‡ĪI‡¸{>ĖĪ_,ŧK@5,`°Ø?…} °˙qØŋXx—€ĮŦöû€ũĶá]?„wÁÅôéÍ.~âÖĒvqü:]üøųÃģø\ü8„w?Ģ‹Ÿ€Ę]`?ņ•ģė?ûņëd?„wũĀū˲˙$œŲ[{6—ŪâyeË*pôw’æYž%˛˙íŗbH’úNF$Cq …3Wļn~ÚgšÃiŒ$YŽdqœMûœūFąMc K3,ÅqWÖg’ųŽĶ Į‘Eķ$Įė§'ę;ĪŗÃxú?š!¯îš4ß1œgH†#(g÷ŗ%ķ}āĐ!‰3>@ũÔúmmūmÕúíëü›',–ˇX,g}æmNJ4^Dœ?ø7ÉŋÕ  Ŗ0âÃŖo°ãr¯kĐ<éŪw—xrfNš}>?ÔØ}ëĘ"č?ļ˜\›ŸÍôˆĶzЋ#1ŖŊtÍE‹l“§Wũ™Axԝ}ĮŲuÎÂë'ãʉüŗ=–NGÃj’d"]•d*B2ŗËĨnčNĩY ģɲRÜÚÄp֟X[K4°)Øe…œ‰5ę—jM„å 9]ÂčV“°čnÛ܉9´ŊXVEËØöRÎ$}¤LÄ$é” *´šÆĒßŪ,qĢKdČÚY4ēîA˛jRĀ“´?aojė,“n™ãžØ‘é–NđËYn$]Ĩ8ęZœ.ĶŋÖޝđņŊÄáoö8?vb3tI­\QĸJȝ‰ÔĻ9ĘŪ÷ßW5éId#ŅG–"Č]e¸tŗŊ7MMjgŊ …ĄMX僤Ą"ÉGē.9C’}„ĐFm)áĒ—¨:BBœö}{Ėz¯‡Ũv€J;‰‘āŖM*5´I-iYZ~>-”Ŋ„˛aÖ|/—t={$ųØFHĪžK­Ē9ÔUņ”$zÃ뉤PšĨ2–}2÷ģĐÆŨŽ6Ęž$‡Ų¯Ę†ė—„ŊˇßĘr˙]~ĩ)Ûv1t˛_•ƒÔôū{5ßę y˙Ģ~øuÜkĶãėûūœ”„ôģ1ūŲOq“ũlj~ÛīOÂū{—pÚ™}?HâŲ¯ĸąėˇņYöëŨĄí¸Û.îŌO˛ôĢŸŠžwÛZ”ũZ۟„ÍÖ*–3š‘5‚6Ķü=ū5xōëv†Øƒķ)kkˇnv?ę‹(˛ˇcŋÖĶuQPģūō ŲíXÕĘ´+ēŨOĩ8N5xåĨ'ŖĢX;;yrš{íöšB¤íZ€mĢ#ųčÕ,īö(‡E}goę-›¨ļÔLį߯!G^ŲõSK64#=Znėļų/´y!÷^g˛õFĻuũ—†¨÷JĨ›žor˜…ĸÍ~ļũĢÍr%û÷.ŨŊHė#t‰Ú¯]ÃâARß Qŋd$õ€[÷É>Y„ĄŊĨGÎŅ~fÛŊ´Åßs>…ûŅöPR¤÷TĐF2­ė¸PU6Â÷RNdWø Š*Úē§đ¸ģ=›(VKZĶĀÔc{ĨŨ{ĨŨ—ũ^‹I.͉5évĘaoĸ…ją;KGÄŦ7q3ũ_u•0GN›_Š)Yiđķ#ĒËiī­mˇM§}Úœ6=UĶ‘Ø›ŗƒd_ ×Ŋ@˜õNŒĄ7č|)ë¯ûPR¨ >2;[*…—Ēn#N1#ËÉ!|ë=´čēœ”-_Ø‹ĩ)L{mkč>īÂÉ^DÜ/IŅ%Âéáj’ęžMZÛūĪ1§?h3Ũ_:>8TL/ (¤&(B"†j>"‘´Ė%õtā"9B­$ŠcÔņ…R|ÔĶ…-*šhĸ>oSĩQ¤đ.›hâ‹TŅŅ\W¨Ēĸ89T“ķ67žTEZzBtЇęŅē´@)ę&Åę˜Â)˛—ÂØWäČÂ\W6¨7Ŋ”ĪÚ¨/ „^"o,p~‰Él Š&bbŲW×häįãH×ËUębG/OŅT=ŋ"Ŗˆ'¨ y"Æ~õūؗK×Ģ>Z-E^O)“,Ĩĸ_ĸ-&Ց&!ŒËįŖļ¯MŠJŋ^A´-E~}…ØHÚ$ēCé hĐIŲŠŪčž br=1TĄhĘvbl„%PĶ*ž|Œũ–,h5™ŌĶĢÔ Š’nēBsYŦûVY°ŌĶ”XdŪĻmCŋ=zfqŖw,Á3‹ŧo+éŧŖ”›ĖN[ŪOŊ;ĻKe„œž0į”õ aUSxŨ- ›qŠ’¸|Ū&ƕlŊ ”]ščŪR`šæBĄāĢbâDIUs{É:ĸBŠž´ÅJ¤ŽũQSŦģę]2ÖÅĻYŽûaã )ļõ˛§OĸŖ—Wū´!zfų.‰ 1p+ }fŠ“¨’[•úÜįTeŖ/\q]̊É2wãĒŠĮ3‘BÕ Z%y›œ_Ĩô5- j­âo$ИÔúúļ.UÜZ‚v]ŠQËĩNŌąIj!kmĮ$›Ōæ:)I}JģCTK ¨zS§s;DšrõIÂRŦÖYŸ+K[ŗĄëŧ'‘Q#ôīvĮåGÄ ¨,‹ļŽo„Ą\ĸôÉF¤eÍÕyCnĨ˙­M1>Hfy>,•eĪo u"ÍfW¨ČōŧÖL6)’ZnũĒM’Snų›úXæĖmčJQtÍĻaĖ‹ęŌ\­úA˛Ø@–<4ąĸe[žĐî{˜E ļT ėvĶč.‹×>H.‡ŊvqĨVz_(f'0ŧU‘W;üÆī)2˛ģFŸyĨÂŲ¸1b”&ęF¸Pēr7ŪLûН;•áĖPĻŽsœEe•ôŠÂ’UˆZ/Žļ šh˜ĖKEÕõ6ÛŅA˛TKÕĘĀŧ’éö]č–ÜZŸ¨viŒŽĘ›Î/NŠŧp'ŒčÚel<õoQæ“Hjú…JIŽōûŖ`dVt{VÃEĨ‹Í01+#sîˆķ^eÉ-P°$’bŧ˜6×F™ËzsŗĒVÔ%žÅjUSŽûą¨zō*ŋš]ĢFę*Ū˛ĢęV_ˇƒ;ŖĘšëÂá5%If’Ô;HÖ 5ŊËQÄZ/Úp#u^›Ôļ“]ÕŦ%ØÎi…ëī’\K_hЉMG-[3jxĢÕ.iފZéũÉT%Ŗ^>oj[´%ĪÕx7EÍP¯Ģ ĩ•ÆÅēŠŌƒŨ„ĢÆL.9šaõ…ʰģe\'–ė|´ŽĸËu[ÛqŖ^ãÕ>;—Kv ëõĸAĩCŸÉįM7ęu†ģUiJųr LÔ]4ŪŨIšÍÆÁr^­ŠĢœ´Aa[ŗŠ]eÔR´‰JF§ž‹8bD3ē0VcÍ1ōŅA+Ņx¨4ŊYu4ƈV<ĶũP›|Ų„î$ÕX'×Ĩ0™wúŨ?‘ˇöĒ<OēbˇPžE“¸ŨĶgĢõTØæT ē;rÚnöąĻsfPęŪU§…šī8’™Ū0žĢ“œ`3g$Ė5"ēsÆÖŧUšĩ‚pîØÎ,ZM N5ģķënÎGōŧåÍæNäÎŖģšĮøün˛ėTvúÂlÆĢ •Īq‹š˛.ÎīĒK$&ũž4_ļÅ ž(W—ËĘŽ^­¯bą[ūĶĒ؊í9z]:^‹¤SøĢâ„ÂzaeåV˜:;Į’Ģ-ÃÎØd¸.¯øbhŦũųŨ°ĘÖäŧĀģB’Ôw÷ļ"W%aA$8ÍNø–lē­úÆ\ˇĩޞY6Jēë’[‰T×ĩ1ŗíM+õڜÜîzÕx™āģjGĶ\ߍzų /9rĮŽôz_d°ÖÎXĮj[–Z†ÖPpyjny+—ÄûĨļÕīŲ8žëPÚpLÔGŨž6ŨĶžsĮ¯RãģsbL'ģ=¯¤1šwˆ ĒäШ—jT؈;-Ÿš¨ģõØŊkq´ŨšH^z¯“ŪEĖ=ËTw3c5ՙp=ĪųéÅ SX/‡+Ė`ģģUĨÎ`,&­w Áæęģ.åĮÎEģ2¨OyŠ…%kKįû,ŅôXžZPĘ ˆîZŖÜRŨfģՀí’RĄ<â+‚)Œ×wdƒ …Bŗ´E9įÕ0įž¸•ŨÆÃļhŒzJqįÖîŪģ—“áۃ፪rqS7ąũŊį6÷ĀŲ­PËIkOfĄMļ˛ŊLĘx˙§dØc„|$ëČšŋƒÖŗ›’-’\Ôԅ*ĘŲ„ƒ„œ$Ŋ*Q9ëŌûU\´ÔĶû–*…$‰¨6FģDę"m‰H]ĘGœÔ\j5„ą’ŠkĻŽ›R[Ö_Kų}avO–īũ×Ŋjtä.Nķüũë§Sú—ĶöšĶ˙T¸üšÃ˙EÉÅ$šĮų­¸ēīãáOķ–ōʐ(Yf>Ü"ߕ-7íöĄ#~Ÿ€Õp|īAæįžž¤Yė…+ž7kEM×y_z18„œ­ˇxØäƒÕ휌E܋œÅ“:œįéÕČŋ"ĸ9“Ŗõ1ûū|;ęÔo,žDA˙/V[ĩ/9%ņRT#Ũk´Š_Žk i0qbīČe{–ĪūĢɖŗđŊø˜rāĮ”à nT ĻR°ŒŸO¸gŅ“Ŋ|:: 'ĸéŗMžDÃöۄûŽÉS?˜;˜~9šŠįÄQ–LSõĻūŖđŌũŽˆŧ@€ā9Œ ‰,iīɎ÷müÅÎÆI4 ŪŧÍĶņR_Ū4~˛å‹ƒf¯cõŠ— ´F%( üc­âGZMşa"ŋĻēāƒįÅ­ ~4jķëLŨpĩLæĪŲ"ŠY‘ ›Ķ`žōæęüės¤_Ö)ž;+XŊĐŗĸ _ņļĪä×qüē ˙||9›UĻėG¸:í{›#;Ǝė­âõĠCâWOé[,ƒ]zjjéø Ļoܑā¸c‘ūL;ƒgåˆĒ šĨŋ3įLüo’,˙ũaÉ ˇ¯¯!I‚üÎŌ|#hžųū`ž~ZRs4Īí~¯iƒĐÛW.ŋ‹Îtí,Íŗöeu:7ĮžMĪSž§2Đčô?+ôß_¸BŪÜr´ÄURĮĪŽũĶgâ Ũđ_=ø ˙—˙û7G˛ūķ”Ē_'øI?€Ā˙IÁOũ†o˙æ–ŖÅö¸ģļRõˇzŸūΝ:¯÷ °õ؇JûŋáŨįûgą÷‰ĢäūÉnøoü¯Ļs™J`ĖŲبųųYsĮö# 7ĶČį;¸†œ7üÃ2ŪøėÅ|DÚÛíĐķįãĩūũcđÎtė˙qg>NĐÄ7r#'ũÁĖ?ßⴍe:÷Ļ›a‡_fĄĸÅû÷Aj’ÄŊđĮû1˜éģ;tK/Æžũ´I4oåũīūņītÛT{Sá˙ũąđúM˙đž7Í>ô•—žīĨūßo3sĻ}¯˙S>ũũĮĖYôÄšÁŌqœØŋ1\öFÔūÃ˛7’Ü‹ü^„ĸö"4ļIˇÎŪ|/°ŠHÖn°˜:ņjá„?ænöCøÃ؛7ŽØ?ūą?ē%I|ûá°oÔwė?GíŦŋ”üzƒø…ÔĮ_-^zļƒĩ‡ŌŅuœØw{üÂ˙ø–ūHņ_?ąīÜY*“ežˆ2Į ÛNzŦO5÷ũ°įöŦ§/ĀzüĘXĐč]ôČī8@īŖ ‡Ŗ.X¸{€ũc؃…û;°?ö­ä­°īöcSäųąßZtæŌ$<ëÕ9ŊØ'ĪÃëöĩû°iČR˙ōĐgŪŧ¤(€ū9RVŽú@ Đ˙ŦĐ˙ K˙ÖęQųĢ„>včķ¯CŸčVčCvú—‡>û–>Đ?G–"vÔ?Ų+Ā>`°õØ˙ [˙æĒQņëä>~vîŸ>oč€˙ęÁOøŋ:øšß°÷9˙yʒŽü€Āā˙Ŧā˙ ‹˙æęQÉë?y~đ“o? ā˙Ŧā§ü_üüû-~đŸüÔu‚Ÿđ=đŋXĻÅfUZĖÖ! q¨Ō‚*-¨Ō‚*­‡UZÄwĒ´>Ē$—ŋëoĒ"÷¯=g ûNŗ É$ASĨ ˙˙ņ/ūÜ5āëßÍWøúaKā, Æ4Ķ`LėÃÖˇų8ؓƒ=õu­ixj1õ’@…5d> ¨,–=Ōũ<ĨÁxãŒg`ũCÖã4 ūÃPO_ õ  P¨Ô?B=¨˙0ԓ™—äĢĶ,xIĀK8ŊNŋŗĀSXiĩÜmŧ9QûÖJ3ßēĻí™WÚ%ŗŅs>úäyxŊS¤}õIÚ,Tį|yčĶī‡>Đ?ôî ĄĒSũ†ūËOģT<”ÁĀsžđœ\ĀsÅ/÷ÎKÅŠ_ øđzx…Ú—ÃëĨâ| <íR: ĨP˙$Éë! îhˇ;ššĩ§}rWéŽæÎƒä^wGsƒü¤îhĐĮŪ}  ŽÄî ĄĪB ōëA˙åŖ.ƒ¤oËiN˙%§9ËP C$•Ūã“1ųæ‰“,í9„Į úė€s ×!ąAßMoŊ]ŽĪēœC˛Î]Ÿcũ¯ ĪtęŠŊÅôÉ~îuėŲ]¸uŽŌ­ š|Üę€ 2Āz`=°X˙˜õ°ÖĘĮąūbŅZXŦÖ럄kaĩׂįūØŊЛ=÷ˇV2Ę_Ĩįž?{¸–ŨsĪC¸öŗzîĄd ˙ū’Q†čŸ#G‡ģBčsŽũzĐyMrėRņZŋøu~G~ŧ:öü ėRņZÖëõĀúGŦį€õĮúKÅkYXŦÖëŸÄkaÉtˆ×‚ëūØŊĐ[]÷ė­•×âØUúîSãéÜÎûĶgâ Ũ÷ũĩģīY(ąđŋŋĖĨügÉÔᮑûp&Ö¨_Ē5R”ƒdTät Ŗ[MÂĸģmCp'ZätŌöbY-cÛ#xė é#e"&I§dPĄÕ4Vũöf‰ëX]Ú CÖĖĸŅu’­P“zž¤ũ {Scg™tË÷ŎLˇt‚_öČrë é*ÅQ×âtyœū…´v}…ī%ŗĮųą›ĄKj嚈UB~M¤6ÍQöž˙žĒéH?H"‰>ę´Aî*ÃĨ›íŊijR;ë) mÂ*$ I>ŌuiĖ’ė#„6jK WŊDÕâ´īۃdÖ{ĩ8ėļTÚųHDhŒmRŠĄMjIËŌōķiĄė%” ŗæģxš¤ûčŲë ÉĮ6Bzö]jUÍĄŽŠ§$Ņ^O$…âČ-•ąė“šß…6îv´Qöũ 9Ė~U6dŋ$ėĨ¸ũVFāûīĸđĢMÁØļ‹Ą“ũǤĻ÷ßĢųVÉû_õïã^›gß÷į¤$¤ßņĪ~Š›ėWuHôÛÖxößģ„ĶļČėûAĪ~eŋĪ˛_īmĮŨvq—~’Ĩ_ũLõŧÛÖĸė×Úū$lļVąœ ČŦ´™æ×čņ¯Á“īXˇ3ÄœOY[ģĨpë´ûQ_D‘ŊûĩÖ˜Ž‹‚Úíô—ÉnĮĀĒVĻ]ņНč~ĒÅqĒÁ+/=]ÅÚŲɓkÔÜkG°×"mĐl[ÉG¯fyˇ×@9,ę;{SoŲDĩĨf:˙~ 9ōĘŽŸúX˛Ąé 8hÔrcˇ­ČĄÍÚīô:“­72ũĢëŋ4DŊP*Ũô|“Ãô(möŗí_m–+ŲŋwéîEbGĄKÔ~íj’úVˆú%#ŠÜēOöÉę$ í-=rŽö3ÛîĨ-ūžķ)Üļ‡’"Ŋ§‚6’imdĮĩ€Ú¨˛v'Eŧ—r"ģÂIUŅÖ=…ĮŨ@Øí)ØDąZԚĻÛû+í> Ø+íž|ė÷ZüHriNŦIˇS{-T‹ŨY:"fŊ‰›é˙ĒĢ„ų8rÚüJMÉzLƒŸQĩXN{omģm:íŖĐΈā´éКŽÄŪĘ$ûJ¸îÂŦwb ŊAįKYŨ‡’B}\đ‘ŲŲR)D¸TuqŠYN–á[õ@×å¤lųÂūXŦMaÚk[C÷éxNö"â~IŠ.NWCĖP÷mŌÚöŽ9ũA›éūŌņÁĄbzi@!5A1T퉤e.ЧÉj%éDPŖŽ/ŒâŖž.lQÉEƒõy›Ē‚$…wŲD_ ŠŽæē¸BUÅ‰ČĄšœˇšņĨ*ŌŌĸK=TGˆÖĨJáÉû‹tYYËÛ4j‚ĸËÔ4…J"oQË~ąˆĖ´;~ą‹Ú÷ķQ7)ŽPĮH‘ŊÆžâ Gæē˛AŊąčĨ|ÖF}Y ôŌycķKLfHH5ŅËžēF#?Gē^ŽĸP;zyŠĻ˛čųE”8A•Í1öĢ÷Įž\Џ^õŅj)ōzJ™d)ũÚm1ŠŽ4 a\>ĩ}m‚HUøõ ĸm)ōë+ÄFŌ&i´ĐJO@ƒ>H ČNõF÷“뉥 ESļc#”(y„šŽPņåƒdėˇdAĢɔž^ĨU”tĶšËbŨˇĘ‚•žĻÄ"ķ6mģúí‰Đ3‹Ŋc žYä}[IįĨœØdvÚō~ęŨš0]*#äô…9§$¨g̚ÂënI،K•Äåķ61Ždë}L ėŌD÷–˕0 _ JǚÛKūĐJõũ -V"u폚bŨUī’ą.6ÍrŨIą­—=}ŌŊŧō§ Ņ3ËwIdˆ[ič3SœD•ÜĒÔįļ8§*}áŠëZUL–¸WM=ž‰ĒNĐ*ÉÛäü*Ĩ¯iIPk#IŤÖסuŠâÖ´ëJZŽu’ŽMR Ykû8&Ų”6×IIęSÚĸZR@՛:Û!Ō”ĢO†bĩÎú\Yښ ]į=‰ŒĄˇ“8.?"V@eY´u}# åĨO6"-kŽÎr+ũĪhmŠņA2ģČķaŠ,{~Sبyl6ģBE–įĩf˛Ié”ÔrëWŨh’œrËßÔĮ2gļhCWŠĸk6 c^T—æÂhÕ’Å˛äĄ‰-Ûō„v¯ØÃ,J°Ĩb`ˇ›FwYŒ¸öAr9ėĩ‹ë(ĩŌûB‘0;á­ŠŧÚá7~O‘‘Ũ5‚üĖ+ÎÆŖ4Q×0Â…Ō•ģņfÚW|ŨŠ g†2uƒät¸(*̤W–ŦBÔzÁpĩUøČEÃd^*ĒŽˇŲŽ’ĨZĒVæ•Lˇī DˇäÖúŦ@ĩKcä9CÆ,-(ī É\Ģ´ŖŽqg–XnĀČReŲī‰ĸ­ÖtŸåünB5ũaŋŠ U—ŪmÕŠšTÖjŒÄ FŠ„ī=ĸZ@ãŌÖĐĘĨd<ß>ëĸ•ģn¸ŲvōqTÜt~qJå…;aD×.cãŠx‹2ŸDRĶ/TJr”ß#ŗĸÛŗē.*]l†‰‘X™sGœ÷*Kn‚%qŦãÅ´š6ĒČ\֛›Uĩĸ.ņ-V̚r܈EՓWųÕ čZ5RWņ–]UˇúēÜUÎ]F¯)I2“¤ŪA˛f¨é]Ž"Öz҆ŠķÚ¤ļėĒf-ÁvÆH+ÔXwäZúBSLl:jؚQÃ[­vIsUŧĐJīOĻ*ąõōySÛę¤-yŽÆģ)j†z]M¨­4.ÖM•ė&\}0frÉŅ Ģ/T†Ũ-ã:ądįŖuÔ]ŽÛڎõ¯ŽđáA˛áČ<%Q~c‚î"‰õ[š`ˇî‚Æ]И0ÖSSī IcōLoĪ„…\Zé#AtĮ\_÷ĨúXãuNf”îīÎfō.;†e'ãŽgeĨ‹9 c-”4ŗĪœsI0‡Õfš¨âظßlkåš9MšŖ~e`.älșØ*×äæ]šVoļ­ęLCcŧŌęj)SakB×cŒC-ėž`cŦĐ7ÅžîZ"kę]Ã,*=Ķë6kV™3—ũ–ŒkƒÜ˛bf->4DKåM7#ĢcYIh7ŦpĶž[=ŌŒNŪf8đÛ"oŦQĩmĖēŽ5ĄÚ~ßąÂų¤Ŋļz†ĩ˛rzßYŽfm•ŽÖí—qœę¸Ą§„ô˛ŗĀÎ;Ŧ–ÛuB[訕™_PŠ Û)ųvšlĪ6§h’M÷G aägž[6ÆLÛäģŨrH6ߝ•'Œâō]Z›2mŋā”ģšÎŗJ(9Ũ،kĪTgÆĪy"n8LQ 6^…EĪ÷Ūë…+yB/zKc]šđT/&RTÜē°ŠOËųt“ŦLܤ¸ëtęT_00ˇÔĒõ›C|Øé û#š˜=*ˇëđ.š$}ÃShjWĪ=;¤irĻxs‹JņČãĘ\N†ÎVԋŧŪ!†ŋxgOYq°Õ ÃR!ôeÍUų~–i¯„­&ūŦ"ōĒÖņŲš\˛›hX¯ ĒŊúL>oēQ¯3Ü­JSʗeĸîĸņ.čNʅh6–ķjM]å¤ ÛšMí*Ŗ–ĸMT˛0š8õ]Ä#šŅ…™°kŽ‘Z‰ÆCĨéÍĒŖ1F´â™î‡ęÜäËÖ t'ŠÆ:š.…ÉŧĶīü‰ŧĩWåņxŌģ…ō,šÄíž>[­§Â6§bĐŨ‘ĶvŗÍh43ƒR÷Ž:-Ė}Į‘ėČô†ņ\ä›9#aŽŅ3ļæ­ŌŦ„sĮvfŅjRpúĢŲ˜_ws>’į-o6w"wŨ-Đ<Æįw“e§˛Ķf3^U¨|Ž[Ė•uq~W]"1é÷¤ų˛-nđEšē\Vvõj}‹íÜōŸVÍVlĪ ÔëŌņZ$ęĀ_'Ö ++ˇÂÔŲ9~\mvÆ&ÃuyÅ{DcíĪī†Uļ°&įŪ’¤žģˇšŌ( "Áiv¡dĶmÕ7æē¸­u•ͲQŌ]¯pÜJ¤ēŽ™moZŠ×æäv×ĢÆËßU;šæønÔËgØxɑ;v¤×û"ƒĩvÆ:V Ø˛Ô2´†‚ËSsË[š$Ū/ĩ­~ĪÆņ]‡Ō†cĸ>ęö´é†˜öœ;~•ߝûkc:Ųíy% Čít°¸CU%‡FŊTŖÂF@ÜiųĖEŨ­Įî]‹ŖíÖDōŌ{ô.bîų[Ļē›ĢŠÎ„ëyÎO/N˜Âz9\aÛŨ­*uc1iŊk6WīlÜu)?v.Úí”A}ĘK-,Y[:ßg‰Ū ĮōԂRAt×å–ęĻ0ëÜ­lŋ” å_Laŧž#|\(šĨ-Ę9¯†Ņ8÷Ä­ė6ļEcÔSŠ;—°v÷ŪŊœ ÷â¤æRĢ!Œ•L]3uÜ”Ú˛æøøZĘī ŗ{˛|īŋîUŖ#wqšį˙ë_?ŌŋœļĪŗáNÅãžįÂŊ(šxƒä#÷ø#ŋõW÷Ŋgũix,O8Œ’eļáÃ-ō]ŲrķŅn:â÷Qž†ã{"?÷ôĝŋŽxŪŦ5]įaāåa€á…pļŪâa“B7΃ž‹¸9‹'ŲĪãŊÕČŋ"ĸ9“Ŗi—ûū|;ęÔo,žÉū/V[ĩ/9%ņRT#Ũk´Š_Žk i0qbīČe{–1õĢɖŗđŊø˜rāĮ”à nT ĻR°ŒŸO¸gŅ“Ŋ|:: 'ĸéŗMž<šdŋM¸ī˜<õƒéąƒéGņ‘Ŗ™zNeŅúĒ7õÅuīwDä)hĪaIÔŗīÛø‹“hŧy›§ãĨžŧiüdËÍ^ĮęS/h/Œ#JPøĮZŏ´šŠ?ÃD~Mu˙ĀĪ‹[AühÔæ×#˜ēáj™͟ŗE ū\8S˙qŠōž sĖWŪÃØß¯géuŠ*ĮÅ –A/ôŦ(čWŧíŗÃųu4ŋŽÅ?_äfûqŽNûŪæČŽą#;FĢ8BũŅęáĀøÕSúÄË`—žšZ: ƒéw$8îØ_dIė?3SN!áYĒûƒLw‚gąīųëE˙ņíO'ŋã<õëĩ΃'žúūāGŠüãÁqĖwü× ãŸfqÍŦšī@ÚõÁ ôöŲíËīĸ3];Ë#öŦŊGãS§éxH˙Ųlņ<Øū<@ũ<Ô~2Ōū<ūũ<ÎūˇíāŲ|˙$ųúŸĮ˛{_Ëžßŗ›BÉ[œ40žRLp–^7Š&oÔßÃŖ‚‰7ÍPąL!øXų%p°$ūĮ7–}RÚø •“Ûb<­’y°Õã$ĻŸÛ°<‘ę5OŊ´]6XŖž÷FZ‹Q¸šL˙ ‰[œ€1É<…q:ĪŌųōŠ•w˙Ž}›Åßūõm˙aķü°ōqļŦ>šÎ_6ą ¯î6ōęØw§Õ7–UĮ3)-xŋޤē}¯pŒĸΗTwōDŧĄWSwí9u4 ÉÔ_~ŦwC˙æjh0üŠčÖyąúTŧĨ_ūĢ?ā˙ōU4ī˙ÍÕĐ`Ėu‚Ÿšø™7€,ūĪ ~đuđãī/Ÿ¤oüøušüøL~ü &?&˙§%?ä˙ōä˙Š)Ė͑˙:m~ü6?ū››˙Ķ’Ÿū˜åÎyn¯uö…O<Ėî 9|’öĘ2‡ØwšeH– š:¤ĸŅaáCü#V<ŧĀ8zī‚o^ëîXōĘßĩä?F^jp0`\É ¸ØLĀà €ApƒŋØL€c76 @õ?™ę_Œ˙8ĒĒ˙ĄĒŪĪņ~äģŊŸø­%8ãÄužˆ žˆ7ž|}Z÷'äēúŠ÷Ŗ˙æŌœ‰ëŒ|ˆ|oˆ|ųúŦčg!Û Đ˙ū'ŧãÔí=áũ*ŅO^Āę'ß`õ“`õZôCē Ÿy?úo.Ķ™ŧN̟ŧ€ÕOžÁę'Áę˙´č‡Ēv@˙û×2Á™Û{DØUĸŸē€ÕOŊÁę§Āę˙´č_? ˙ũ+šāėÍĄ˙:­~ęV?õ̟Ģ˙ŗĸŸ_? ˙ũkšāÜ­ĄŸžN̟ž€ÕOŋÁę§Áę˙´č_˙—G?ųūEMpūæĐV?}̟~ƒÕOƒÕ˙iŅž~@˙ûW5!°[C?sV?s̟yƒÕĪ€Õ˙iŅž~@?ņ~ôã7‡ūë´ú™ XũĖŦ~ŦūΊ~|ũ€ū÷Wķ7WÍË^§ÕĪ^Āęgß`õŗ`õZôãŗ˜!N\lų‚„5L` “ .ßC\lų‚ÕÕŋ ę“—Ŗ> ĒĒIÕŋõP}Pũ Ē>u9ęŗ ú ú—TũËQ˙V,Į˙‚Οj´ūŦZO_ø‹“ôß8ęÚ˛Ô°}ˇ^Ožû;Ÿ¸{ęLŧĄ[Ŗví9jÔŖøß_™BŌūŗ€Ÿ¸Nđ~?€˙ŗ‚˙ũu)äÍ­ßÁ5dŋá–ûÆg/æ#āno„ž?3ī¨Åđī‹x€˙pĻË`˙;ķq‚&~¸‘9韆dūqø§m,Ķš7Ũ ;ü2 x-&Øŋ R“$î…?؏Á4HßŨĄŗXz1öí§Mĸy+ī˙ķ§ÛĻڛ ˙ī…×'hú‡ŋđŧiöĄŽŧô}/õ˙~x›™3í{ũŸōéī?fÎĸ˙#ŪĐ –ū‹ãÄūá˛7‚ öo–Ŋ‘ä^„ä÷"ĩĄąŊHēuöÆā{†ME˛vƒÅԉW 'ü1wŗâĀÆŪtā¸q´ĀūņũŅ-IâÛw€}Ŗžc˙9jgũĨ4ČĐÄ/$AūjŅđŌŗŦ=”ŽŽã„ÄžãØãūĮˇôGŠüĘø‰}įžČR™,ķD”9NØvŠĐc}xĒšŋ{<ƒ=G\öø-Áū/×ŦsäŖ åëØ –üŽ`? °ôÅK€5 Ö4XĶûĮ°kúw`>ķ[ɒaßí3§Č[ķ™ŋĩōčŧ>sžK{şÕe~ōDŧŪ+đ˜_ģĮœ†Ŧø/}æũĄRŠ럧ö*š˛[~?€˙ęÁ˙öūÍÕÁ× ~üüā'Ū~Ā˙YÁYņ_üėoXü €˙< \'ø ?€Ā˙YÁ˙˙ÍÕÁR× ~ōüā§Ū~Ā˙YÁOøŋ:øšß°ø9˙yVžšNđS~?€˙ŗ‚˙7,ū›ĢƒeŽüôųÁĪŧü4€˙ŗ‚Ÿđuđķi Āž%ĪŽü €˙ë˙å’-l_˛uhJļ d Jļ dëaÉņ„’­ĢĪ%.ûkĢĪęõމzPīèG]Œz$˜¸`âėöaKĐ|뙋Ážúēk|Á㌨—*,ķōa@åšĸü%xJƒņ Æ3ĪĀú‡ŦĮi@ũ‡ŲÎy)Ö3Āz`=°X˙ˆõ°ūãXeŠ@XĒ—f!Đ蝂Ūw¨kÖBšķ‘{‚7§9ßZaã[‡=÷څxvŋxÎ;Ÿ<oč$9_}’3 Õ-_ûôûąĪöĪąR9s…Ô?Õ)€ū C˙ĮĨ"— đp€‡ã”‡*<>đ—— á18@ Đ;=(đø@č]*–ÅĀc'!oXŦÂcöÂ_.ƒŋۗËÜŪc'¯3†‡Ÿ=†wúLŧĄ[āĪŊv.O#đcī?ā?Oōs•āĮ ÷õĀ˙Š{įb<|Úāį?Į)?ŦcņKĩ],ĮôzŊSЃĩ&>z äAQ@ w:ĸË.@D ›ĮŒã7;6oîi{øu:6‰ķG´đ786 ˆh}VĮ&”Ĩøß_–Æđūķ¤20W ~"Z_ü¯ø9.ŅbĄ4 üāį8åįāÁËņaž]ōR-*Ķ€yĀŧSĖã€yĮŧKÅŗXxŪ0˜w:žKËB< ܚĮlãˇē5Ų›ĢĐ"ŽĶ­Iž?žEŧÁ­IB<ë“ē5Y¨ĐđŋŋB‹ĨüįId`ŽüÄŗžø_ņs\,žZāį?Į)?õ—ŠŽrÄ׍Žū5Øcœū­QxŽ'FÁ?~ėnã­ūqîæ ŠëôĶįŒRođĶũ¤ūq üī/$ä(˙y2b˜Ģ?Ņ¯ūW\( ŒŌā-o9xËÁŊķĐŊYīį-g.eõ€z@= ū!ęÁ˙¨ŋX`”Ôęõ€úĮA[xp5mÁwėNčÍžû›ĢfĨ¯ĶwΜ?hKŋÁwĪ@ĐöŗúĀ˙ūjVŽđŸ'[‡šJđĶ´ũzāÅŊsŠ -ÕŦāŪ÷¸wģwĀšķaž|öRA[ĒYõ€z@ũ#Ôc€úCũĨ‚ļ<¨Ôęõƒļ{ڂīū؝Đ[}÷üÍUÚ2×éģgĪ´eŪāģg!hûI}÷p&Ö¨_Ē5R”ƒdTät Ŗ[MÂĸģmCp'ZätŌöbY-cÛ#xė é#e"&I§dPĄÕ4Vũöf‰ëX]Ú CÖĖĸŅu’­P“zž¤ũ {Scg™tË÷ŎLˇt‚_öČrë é*ÅQ×âtyœū…´v}…ī%ŗĮųą›ĄKj嚈UB~M¤6ÍQöž˙žĒéH?H"‰>ę´Aî*ÃĨ›íŊijR;ë) mÂ*$ I>ŌuiĖ’ė#„6jK WŊDÕâ´īۃdÖ{ĩ8ėļTÚųHDhŒmRŠĄMjIËŌōķiĄė%” ŗæģxš¤ûčŲë ÉĮ6Bzö]jUÍĄŽŠ§$Ņ^O$…âČ-•ąė“šß…6îv´Qöũ 9Ė~U6dŋ$ėĨ¸ũVFāûīĸđĢMÁØļ‹Ą“ũǤĻ÷ßĢųVÉû_õïã^›gß÷į¤$¤ßņĪ~Š›ėWuHôÛÖxößģ„ĶļČėûAĪ~eŋĪ˛_īmĮŨvq—~’Ĩ_ũLõŧÛÖĸė×Úū$lļVąœ ČŦ´™æ×čņ¯Á“īXˇ3ÄœOY[ģĨpë´ûQ_D‘ŊûĩÖ˜Ž‹‚Úíô—ÉnĮĀĒVĻ]ņНč~ĒÅqĒÁ+/=]ÅÚŲɓkÔÜkG°×"mĐl[ÉG¯fyˇ×@9,ę;{SoŲDĩĨf:˙~ 9ōĘŽŸúX˛Ąé 8hÔrcˇ­ČĄÍÚīô:“­72ũĢëŋ4DŊP*Ũô|“Ãô(möŗí_m–+ŲŋwéîEbGĄKÔ~íj’úVˆú%#ŠÜēOöÉę$ í-=rŽö3ÛîĨ-ūžķ)Üļ‡’"Ŋ§‚6’imdĮĩ€Ú¨˛v'Eŧ—r"ģÂIUŅÖ=…ĮŨ@Øí)ØDąZԚĻÛû+í> Ø+íž|ė÷ZüHriNŦIˇS{-T‹ŨY:"fŊ‰›é˙ĒĢ„ų8rÚüJMÉzLƒŸQĩXN{omģm:íŖĐΈā´éКŽÄŪĘ$ûJ¸îÂŦwb ŊAįKYŨ‡’B}\đ‘ŲŲR)D¸TuqŠYN–á[õ@×å¤lųÂūXŦMaÚk[C÷éxNö"â~IŠ.NWCĖP÷mŌÚöŽ9ũA›éūŌņÁĄbzi@!5A1T퉤e.ЧÉj%éDPŖŽ/ŒâŖž.lQÉEƒõy›Ē‚$…wŲD_ ŠŽæē¸BUÅ‰ČĄšœˇšņĨ*ŌŌĸK=TGˆÖĨJáÉû‹tYYËÛ4j‚ĸËÔ4…J"oQË~ąˆĖ´;~ą‹Ú÷ķQ7)ŽPĮH‘ŊÆžâ Gæē˛AŊąčĨ|ÖF}Y ôŌycķKLfHH5ŅËžēF#?Gē^ŽĸP;zyŠĻ˛čųE”8A•Í1öĢ÷Įž\Џ^õŅj)ōzJ™d)ũÚm1ŠŽ4 a\>ĩ}m‚HUøõ ĸm)ōë+ÄFŌ&i´ĐJO@ƒ>H ČNõF÷“뉥 ESļc#”(y„šŽPņåƒdėˇdAĢɔž^ĨU”tĶšËbŨˇĘ‚•žĻÄ"ķ6mģúí‰Đ3‹Ŋc žYä}[IįĨœØdvÚō~ęŨš0]*#äô…9§$¨g̚ÂënI،K•Äåķ61Ždë}L ėŌD÷–˕0 _ JǚÛKūĐJõũ -V"u폚bŨUī’ą.6ÍrŨIą­—=}ŌŊŧō§ Ņ3ËwIdˆ[ič3SœD•ÜĒÔįļ8§*}áŠëZUL–¸WM=ž‰ĒNĐ*ÉÛäü*Ĩ¯iIPk#IŤÖסuŠâÖ´ëJZŽu’ŽMR Ykû8&Ų”6×IIęSÚĸZR@՛:Û!Ō”ĢO†bĩÎú\Yښ ]į=‰ŒĄˇ“8.?"V@eY´u}# åĨO6"-kŽÎr+ũĪhmŠņA2ģČķaŠ,{~Sبyl6ģBE–įĩf˛Ié”ÔrëWŨh’œrËßÔĮ2gļhCWŠĸk6 c^T—æÂhÕ’Å˛äĄ‰-Ûō„v¯ØÃ,J°Ĩb`ˇ›FwYŒ¸öAr9ėĩ‹ë(ĩŌûB‘0;á­ŠŧÚá7~O‘‘Ũ5‚üĖ+ÎÆŖ4Q×0Â…Ō•ģņfÚW|ŨŠ g†2uƒät¸(*̤W–ŦBÔzÁpĩUøČEÃd^*ĒŽˇŲŽ’ĨZĒVæ•Lˇī DˇäÖúŦ@ĩKcä9CÆ,-(ī É\Ģ´ŖŽqg–XnĀČReŲī‰ĸ­ÖtŸåünB5ũaŋŠ U—ŪmÕŠšTÖjŒÄ FŠ„ī=ĸZ@ãŌÖĐĘĨd<ß>ëĸ•ģn¸ŲvōqTÜt~qJå…;aD×.cãŠx‹2ŸDRĶ/TJr”ß#ŗĸÛŗē.*]l†‰‘X™sGœ÷*Kn‚%qŦãÅ´š6ĒČ\֛›Uĩĸ.ņ-V̚r܈EՓWųÕ čZ5RWņ–]UˇúēÜUÎ]F¯)I2“¤ŪA˛f¨é]Ž"Öz҆ŠķÚ¤ļėĒf-ÁvÆH+ÔXwäZúBSLl:jؚQÃ[­vIsUŧĐJīOĻ*ąõōySÛę¤-yŽÆģ)j†z]M¨­4.ÖM•ė&\}0frÉŅ Ģ/T†Ũ-ã:ądįŖuÔ]ŽÛڎõ¯ŽđáA˛áČ<%Q~c‚î"‰õ[š`ˇî‚Æ]И0ÖSSī IcōLoĪ„…\Zé#AtĮ\_÷ĨúXãuNf”îīÎfō.;†e'ãŽgeĨ‹9 c-”4ŗĪœsI0‡Õfš¨âظßlkåš9MšŖ~e`.älșØ*×äæ]šVoļ­ęLCcŧŌęj)SakB×cŒC-ėž`cŦĐ7ÅžîZ"kę]Ã,*=Ķë6kV™3—ũ–ŒkƒÜ˛bf->4DKåM7#ĢcYIh7ŦpĶž[=ŌŒNŪf8đÛ"oŦQĩmĖēŽ5ĄÚ~ßąÂų¤Ŋļz†ĩ˛rzßYŽfm•ŽÖí—qœę¸Ą§„ô˛ŗĀÎ;Ŧ–ÛuB[訕™_PŠ Û)ųvšlĪ6§h’M÷G aägž[6ÆLÛäģŨrH6ߝ•'Œâō]Z›2mŋā”ģšÎŗJ(9Ũ،kĪTgÆĪy"n8LQ 6^…EĪ÷Ūë…+yB/zKc]šđT/&RTÜē°ŠOËųt“ŦLܤ¸ëtęT_00ˇÔĒõ›C|Øé û#š˜=*ˇëđ.š$}ÃShjWĪ=;¤irĻxs‹JņČãĘ\N†ÎVԋŧŪ!†ŋxgOYq°Õ ÃR!ôeÍUų~–i¯„­&ūŦ"ōĒÖņŲš\˛›hX¯ ĒŊúL>oēQ¯3Ü­JSʗeĸîĸņ.čNʅh6–ķjM]å¤ ÛšMí*Ŗ–ĸMT˛0š8õ]Ä#šŅ…™°kŽ‘Z‰ÆCĨéÍĒŖ1F´â™î‡ęÜäËÖ t'ŠÆ:š.…ÉŧĶīü‰ŧĩWåņxŌģ…ō,šÄíž>[­§Â6§bĐŨ‘ĶvŗÍh43ƒR÷Ž:-Ė}Į‘ėČô†ņ\ä›9#aŽŅ3ļæ­ŌŦ„sĮvfŅjRpúĢŲ˜_ws>’į-o6w"wŨ-Đ<Æįw“e§˛Ķf3^U¨|Ž[ü˙Û{ĶæÔuŽ[ôûû+Öį›Ŋˆ:ÛŌ=ĪsĒLßš@íĒ[Ķ÷„æÔųīW§LV!8Ŧ™Ŋk%!˛-ÉŌScNMbOŅŅMzĸ‡æZx4)‡xœLO&ŠU.›MCe×ōī§‹…ieDôZU™>…¨™~´gŅCĩnjVOŠ9m„7%gKUjķæSr&ĸ5r÷dnšiíö‰ŽnE=8ŸįVĪļ"ˇįŨÛáŲĘ\"Åz!ˇ(>E—™jl1š‹uëvSrω§LG]ÖúŠ\fD—ĢZz:™ãUú!›­ŧj×\†N8]im#×Ѝ°Ę?Mˇh/äŗw1é—ĸä–čxšÔ¨U0^=°lŗCríj-Û_~Íŧ3i|?<ŋŖ)2hĩfÅŗj‹.ûã°4mæsņ ëŪĩČMÖe.vķԊ߸R)ô–\ëČUÄȲ—jz5ĖĪú†Ú}šøiMįęíͤ9Cy­ēšĨr*ŌPøiuŦđÜÃĸūwÛÎĢUė1×áš?• ŅĐHíąĻ 6fąĮÖāĻĐv-ÕÅíđáfö¨5nįņÛd[¤‰zÛyēĄwbz{{{_ę.Î'ēƒŽĢÄÍ*eÜ-‡ōíZ,ēĒ“ŌęYŨs‘áYÁ3ídt‘+ĸõÚsé*p•B7ë"mĨ7ėVhÁyJ/‰/%­JG×m=bčæķ Úp%K=\×ī`FFœ…„ŠĮtŨœË%PœšX'×1ļžĒëCŽ[ŌLŸëᐞéčĢy¸Ēg':5ÂîŒ ßO˛iáĸ‘-¸.G˛ĻŸÂîēĐY“šO]ĢŋČ´úáÖdēī<á{Ū“uyI‡æØœļũŊKvŽ&Y_Ķ]W,Ōˇ[}¯Æ4SÖô-s:pŧõiĢooųuŸDÜ4"8"”ļ÷āõ=>YŲé|0l}øšŨų’ˇŦūtįĘw'ÍzŒåú–3ŅŪ™5ųÁ\īļl¯ģbģĘâ{0ážĶ­ęođÁ˛Ļ…ÖtkÖēīŖ>čOäȰúõåīáx0xÜF §xąßÍŦˇ~ŋםš5+B¯f”Z“V­k•­FĘZî5åĩ%¯īá?Û/D¯;Ņë9žč7Ŧ…Įƒ‘ĮƒõŲt 7Úŗˇ“âĩĻʁ+&­•ėšŒœ­ū4ë{ė°ŋDĨ‚ƒŊ0÷7QîDĀDųį×o,˜āôõË Ü”f¸(o>ũįŖD  7ímzŌ<ąAĀQ×ÉQ ėã…Ĩ”v|Â>Ŧ]_ø„œĢ„+XŅ0ÎNT†I@"#įT0ĻøË1…ąÄ²š†4*ëLU.‚eK0AHÂ:ųÎđ‰ã:ņÔM} M1¨áüõ„#đ „seQåXáĮpįĢDሊk¤ÃEA˜ •K{žsŸå6Â4 4Æ$ēk\¨ˆŦëLd ŌŖ°ŠĻ*߯7Įöአēņ9ŨüÁ´ēųč†rŨ\ŲV[Į°V5Ļ Ä‘ĒJˆãĪPÉbS.ˆÔ_ÕD#‚2Ļ*H`J… ĪŒĢŽZ%Z6ßŲ‰'ˇአįŊŖ‚œ‡E~ÃÂY“õˆãj@ĄŠ4OÜHOæœÄĐ?ŋú꓀âŖâ}ds?¯…Zãz×ķ /Čû\é‰Uĩ]°qO|=biķĩuÚáËÕöIWOēڃį_zw2ļkDîk›îpŪĐqį ­š.É1&ëo*wžÂÖß䈔ß(]Ąb]DŽ’œo Z‘W;ߜČį›sT‘sßÖ¸oNgcŗûîÁE”l.bü}§9÷Ė[˛ŋ[O–.į—7āĸŪ™øŸ_ōC&ļŋÄõ˜šNYu§¨ęÉpz,VŪĒ8aąĸ^[Ūo THŖš*sij;VŌŽŸZZڔ MĄūZŦ0Ŧ˜B‘B…BfØņĪkō3.×TĨŽDÅđ7æũ>Ž Ol,TüžP7ȆŸĸŒiז$u+E'ZK2DŨZb &ü|TßF xĢÎ*Rŋ1Męq}xjƒ€nŽĻ›-Ä>§0›v€o¸vßp⛋;ū¯o(đ đÍg1 {8ž7âžß#ĀŽpĀ#æG hāˆ9Éķ.w¨^ÆŨ36ž;8š.î@Ÿā ü)f@į`‚ Ā:Ž?°ˆ{6ÄĨœī#.Ūļ֝4Žß¸ . . îL\x{>ŧĨ~Å[đđđöxËo·ˇžU4Ÿá-čŦŠžZö3€Eˆx‡ oÛüÃA!üʂBž(Āâ{ƒB¯EŸēˇčûļ÷#ûđÔAPˆīƒBŪ?žų;øæ„tw\ß\<č]ßŲ āā⛐íîøāX\؁JÔ+`›âŊ<&~â˛Sg¨˙u|sdžÚ āßķÍ ›zßu3xŊSzųėCƒ›Án†ķ$ļįÄœrރoâl.[‚ŊČ׈ƒ‘ĀŊÄŽœÁ –Œ˙7Aüø͐regŅPĐT„Ą2Á1Åüä?#J@5ë|cĒ‚!nž0 )N• ø;ķŸŨ‡'6ÄߟDCÎĸ•mķ~s>ßåÍ{/`ķĖ{0īŋÁŧ‡-ž`ۃyī°;>á$ÁâڎšÜ>‰W¨x}Jĸ¯ĄŋU¯ˆŠúŲJáY“GöâÉMßīūĐSN›|ĪÆ—ë?¯1wy‡¨WƒbĶî[›N(@Éīėāe/+Ŧ Ž\(ĢXĀĒĀa­ã#ō ÁWļ*Ø1gĨ9á˙ÉßxˇÖâ˛1’G÷âÉM‚U˙WįQūkžcÎĢa’`ƒ˙Åa’ x0āÁ€w愨B¯Ė€˙"[ø› x´]izá“=Žîœ›ŧ˙ xļÕūõœCé œÃ€s.Ī9yUúGsΑMΚfÎyW4š׈ņAĀ(Qā@)8P ‚HvRė+ä9=ß^âߘx-q/Ÿ˙ŲK‚Ũ°Û÷RKÃ'gD\‚đžšîƒNŀ¸€¸€¸@\83õœ€Ģl.õ ŪÂ™Š€ˇ€ˇ—Ā[ȖsNŧ%^z´$J!ˆ”V@Ńë~8hÂ!4ä€mūáĐzmĄ!_eņÍĄ!ÔcÕwš#>ŽîÄS[!ž y? įī`|ã(Ā8>€į^ŽM9G6 888Į˙œŖœ5^Ųššŋšg/ŸgMĐŧ*}ÉŦ ĮöâÉMÎņ=į0œœŖžĀ98įōœ#ŽspppÎÕæŒ>aŖ/ģ˛ķsŋ*ũō÷rķJŅüyĸü:Î9ļOnpŽ˙9‡įį°8į\žsžhqæ'Î9˛IĀ9×Ė9īķǚ׈á—ĪHĘ$—€äō !Ī'„<38Báœ;A˜×!7~HgÍ(lŊƒ­w€¸—Čyq·¸ÜĶ\÷â2Ø{†(ĀâAXCöہrîP…&NPί-Bč‹<ŋ9*yUú‚i™íœ›Ęšī•sŽÎá'xkœãoíõqÎÎųÜų‘^ōŽPÜ/(¸ƒä’—˙ûƒ'āŒžá[*P Ā÷ ˆ{ Ä'ÃW#^ˆëƒŧ§šÄÄÄŊDJU ˆ{Æhŋ".g€¸€¸€¸—@\ˆ{6ĕ( LeLà Æ3ōĪ/‚X@eŠĒrŽ N1מo1ƒĖū ĩ(Œ.ĨõŒ˜H}Љq ˜˜xA =#&ĒtąâL„0X—Ü^d]Ž\ØáŅÆŪĻųGŖ1S¯+ژqSAÛ3ükĖųl•ŋ,ŌøČ<ą9eė÷(c/kvļüu\COā ¸æÂ\Ŗ Ģâšãš\sÍ\ķžDũępä $BŌ‚˜ČHgĶíŠ_}™Á p p{ ¸åˇ Úƒ’"IHEĮ+)ʕĐMčļ üķ _ )dGšPč1ÚĪ— )Įu≭Å÷:ŠĸfLŖœĀ4*0Íe™ķũ˙Xĸ9Ē1Ā3×Ė3ī HīčGŠúčG ]"ሠŌŲôz˛ãu–~Đë{`ī€â!PÔ·‰ģ>LæL€‰€‰€‰‡0ėÄ3bĸęá.đÁ~L&&&Ū4 ųã üœbėį;ÅT|eNą/ føŪø mš<˛Ol8Æ|ī{?Ę ¸æīā~×⚠s En‘ŸË5Į5¸æššæ=ĩīzũ!€Ģ¤Ä{ āë|8öŠSP…$­€‰€‰1‘&žÕũX]?ø€D€D€ÄÃ>A8á|‚ Ķ0Ÿ?ŽĶ^ŲFš/ŲvöŊåEŲådÚã:đÄրJë•Ō¨͍'¤QU5`š Ÿ }Č%šŖ<<<ãžĄ'$™"W–dŠíĀ$žĪũ!vŌc;‹€ &˙8˛Olpīš†@š)āzBš)ĸ×\˜k~ĢUĄŠB8ã\pYõÍ5Į5¸æššæũ}ūãÍ!=rMAŽ)p_$ׄPž/\Hđũĩ B( 䚂x!Ń ÎxÎũ& Ÿäß#k 00ņ &Båų0Qķōø ޜBŽ)ĀDĀÄwâĘ1 "ĕƒgĖÛ~ū°gŒ^YŽŠ¯ iøVΘē įĀÆãũŽėÁ›ž1ß{Æ(䚎!'äšĸ¸æŌ›˜°¯61Ę5Į5¸æššæ=ĩį7ŪÚņƒN!Ų¨= öTĀAë9Ÿū[ņ+(Bļ)EŃ ˆĪŠÄ+žÛ~AČ7  øŽ_`ü‚ Õ0 ?ŽÕ^ŲŽš¯Ų~öŊ;æ¨×Âėrbí‘]xj{@­õŋZ š@€mč 9§¨lsiĪ ßß úƒÉæ¸æ×\3×ŧīDģ։/TpDpĐ{@ī9¨÷P{Î'‚cş˜8`"`âAL䀉gÄÄ=Ī /ƒŠ y| Āí%\Ž`ƒ‚Į4`oÃüŖ°rm 0Fv‚ĪŽ˜zŦ÷.¨Ų‡§6T`ŋĢĀÖ.8˙6˛9>ēEá@6'îŗč–“ɆCx ÍgBĖ}ęq $’JŌ”$ „¤3îčņ§3SEˇˇˇ€[āt{RÆ˙›ãŖĨ•]™”"­=3aO`>Û(äQg|A)åČ><ĩA Ĩø]JŅ0čö=ؐãÉF˛š8Ų(×F6  Í'„$ĻíeĀ}ˇ¤a’@H!éû…$€ü„ $qī÷ÚŅÆŊF¯Ė¸˙U曕¤/ $ú:ãūČ><ĩA`ÜûŨ¸į $ũõd#ŽW’4dsq˛a×F6 ČČČæ:Éæ„•Íĩš-”H6ŠĪČF9•l ›ë$dķ×'ĶD',mT`›‹ŗzmlŖÛÛÛ\'ÛāÖ6âĘØFü@ļ>cq*Û`›ëdlGˆŋļáØæōYĸĐ:+?œmŽk°ÍŗÍģĀÔ#_~'9ŋĒ`üŗÂ!8Ä%ûî„ā…¸ä3ž›îS p:€"€âAP$ŠįEáSPd°96ĮŪ^oá@ö3â-õgQGt  x0õœ˜xFLôgĻOŽ&&&ÂD˜xFLôįą…\LLLu ÂnA{@î9(÷ĀņĸįLããSˇ ėLL<ˆ‰U✘čSˇ ėLL<ė„´āĨÖÛzū°R{m앍Ô*>s *§*ĩ ¸¯SŠ…Ũ‚@6ĮīČæō1(üĘȆ[ČæręOˇ \:‚Ūzč=ôjĪų$pUņ)&bČdį<ŧđ8#øĶŠl‘LL<ė …tŌā yúĀ’áŖú4F×ļIRũĩę3o¨zĒ@­‚7ô*j/ĢÜĄß ø†ß\>ú†_ß(āžųŒęŖųÕ# ;%AõÕįę§åœQ ×üę…’€‰€‰‡0RžũęÔ Jä‚Q"u€[ŋ8!‰?8A>`™\žļm˜Ú‚5Ÿ9ĩS…` W*Ã>Lā¤œĀ7øæō.üĘøFĮ#đÍg%îSĮ#†­˜ ˛ƒęsHõÄ[gŲšONnLL<„‰°᜘čSĮ#&āxĮ#Āí%pR.8A>`™XÆ×ļã‘˙@!˜ûĖņČO‚98¯SưãøŸ°ã3ā›Ëēđ+㠏Ā7Ÿ”„_°ãDvP}Š>l~F‘]øÕņ;a"čāįÄDŋ:aĮ#äÅxë…ƒĶÁ# õ%ÃĮękۊ)~ B-|æ§*Ô<ĸWĒPÃVLā|ÂVL,€o.Ã¯Œo8xDo>ŖtɕÚöˆQüĄū؊ Ǎ>UĐ|Χūc¤øa+&`"`â!LD€‰įÄÄ—¨â("{Á;jíķųÃj-šļũ’rîķšĪÕZŒčūēė‚jí‘}xjƒ@­õŊZK`ŋ$đ 9aŋ$aĀ7—Fņ™wđdžāžų„ęC9 Á˜ĀĒ&ר”Ë•&Ą))*ŅĻÉĩˇŗPÚĀëĄ%d)9šdIÂESĘUÄFÖøW‹B_HcĩŒāĒĒjŪ,fēF(D0īkÆ¯Ä _üãh<@dũČ0 =´ÍĘûđŲvlTØ"đ`ûļWˆX‚„P\Õį“[>‰I$ĸøŊËŋä—[mėšÛöäéĸ¨ŪmŲ}oÔß3/¤Œęüm>FëŅAeõßúÄyA˙ÖŅæ˙ī“ZęŦ*rtVōĢ<wŦņwoTžëĘĄŧkûžŒoĪČEÚ~Ą5¯KÂßeíSWĨM(Ōnā‘?{¯H˛D@AT0 LĄ”=ņSė ė °'Āž{뉓í öÄ{öAJ€Ė1áœķ}s‚*ksBšŌp ˆÉĪ1W@žø1æsĖ 0'œsâds‚€9ņ~ŒĢĒcØ™ĻØ1ūųõ›ÉĪ4Įĩ!TMÕ¤Ŋ@ÖVÉF P¤Ũ@ĒĒ˙üĸšp,…)Ō„Ē`PøĐ  `P€A`PoP„Y^ŒũdS aS`Д7…X‡PPL„pĘ’„1H?Åĸ `Q€EX`Qœ,Q ŋLĸx\ģž{lÜjl3ō›!ũRâ^NAyģ7ö@‹uéŒŲŒKvQhĪŽņlˆ„/ģÁכ˛ųÛ3̚îĪ•~pb>dQ&.ęF"ԋąā|XIÆĢŨz?;ŦĨē)™ŒE—Ō6zĨe)”G5Ŧ$cĸeöJíFmVË-=ž˛õŽwô ­/dŠf…fį…RÖíĪ’î|“ųbÆŽãdܰõŊ¯MI1­čēáü.¤‹M#:TR˙Ā×NÉ`´]'‘ķSqũˆl§úm;ŋoJ6Oc ڈ×ĨøúĒ|ˤëßCÁ×{ķËr´k:ŸÆ6ĨúĪŋ§ŨĢ6%Ÿ?56ŸvjeĨãüžî“xPūžīŧÔ3´p>M4IŖ\ęŦ;aũ{•˜åu~ߔÄΧĄü¤QÆCįĶ›ÍŊ§Õrt%Š„_ë)Įyĩœ8Ÿf֝°X–ĸI§@äΚ‰žčģīhûĶÖÎī¨úĐDoú3’}ĒĮģKŗÜ4Bú ˛ėؙBGɅ‚‰ęCc˛)Y}ČŖtÉ]ĶfŊgØrOåžY˛3ĒąŌĒ2ßyG÷ëŅŅZ"īG˛-´Lˇ#žo3šZĀH7jŦ*‹\ĄBŌ…„3æ!_ÎûKl—ŧËæelFÔdQ)—ö;÷ÜːįJ?9eswÎøË¯#$ņ\ –ĒĘūĻM؊Xvørī×{&SÎŋ7ōņ!˛"ųnd^ÕéF7%epЈįįšjĐM÷ēŨĘRi›žõtŽ{éĪāķl{[2¤ŦQ!ێ(ŲvešiąE"’īV{Q\“8áŧáMÉD,ûT‹ \oWkŧ×§‰xö>^O˙Ã}ß Øîû~۟GņVÉIąWęU’ŨZ/ÛMDĢC9#†ĩ^Ũ˙ŗjŦëÎ#ŗ,f ‰Ŧ^#xŋEéhRÖž´Ŧ–YĮ`ŲAŗŦôr&ÖzųáĻd#Ö}Ēĩ‚Ãځ9ô1wę[[2˜ëÜÚzņaÉ$ˆp9tīĻf"‘ųD×ņŌzS˛ôfŦGæÉ’\ˇĨ´¸í×ĘĨf}wžÖbĀ_K†ę¤Ûßŧ`¸Ø5ė --/sÎxsOų<9?¸•¯F2=1×zé[§zxâ–4äÄÕ#Ŋ0—Díčv°­ĮlŊf—zŧŽ?J¨wī™¨č­šīdQīŲĄG=eč##4ĶĶ }:q=qīš°Ãi=+;Ä×ôœŽ+FxŦKđvXĶH08dŨ{æ3Á˜yÔī‹ÁÔ<˛Ô vđΎFõĸŦŽ­ęåg>ĒÎŖ3ũĄ|ÔcaŊ2 vė˜Š›‘āȈ-ôZ'87â.këHņĻnu‚ÜŽĢŽ ÖEŊ‰BI;ņ¤ˇmwF2­wĐƒ‘ėëũHȲS}ĀB==ÕŌGķĐÔN?ˇ}2 a#mëŗIHeæ“pÔÎ4õ% įôlXGÜåŖ˛íé4~´s)]Š„vnĻkƒđb~WĐotŲwĘĻdP¯ČqcØÁŠäæųD0ZŒTæųE0Î"mũŪ ĻėČĻäÔ.D‚ŲL„ō-ŨąhØ(փ÷“hÎ.%ƒ%ŲMķuīYŠDģvšŦŖ ãĄ´ŠQaWb’wbÉy…:ŨæÖ͍Ž‚ũIŦ­›āˆĮæz-œeb¨Įƒ‹N<5¯ ÷žˆĮ+FY%Ū3ŦIPãqd?vƒˇv"4ˇCáDÂĩ—ėĻŠą„mˇĘĄÔ ņdˇīCšzâfŪ1B÷ÅdÎîŪmJ†ĘFŌ2zw!ĶHÎėū]Č*&oæƒ|¨UOŨÃb¨7HšVĨ1Ē„F,ĩ0ÆõĐS&šOZĄU']4ĻÃĶĶ=}6wīÉí43ž”p0‘IŲ‹p8:Ī4Œe.œĒgæúĒžË¸Ŗ.l ž´ŗeŖp…eG ‡,{ŖŗB¸År÷†âÚ!á>Īõæ* O9ÍæÉđ˛xg ĶÁ]מY…9w[¤õd$T1ŒE°‰3Ŗˇ)‘lŨųHAū—/,ĸĶMIį%šņdÄ˛īƒ‹D/Ō)ŪWƒŠHd”šŸ/$:Í3Žõ›XdÉ[ö"׉đbAÉąh¨^ŧĪįGŅĤ8Îr›’Ņ;ŊiQ´T)YÁr-ZC%Ŧ„Ŗ­Jų>_Dŧŧ)9iÖĘҧ´ŌÁ()>´ōÖ,*ba×bŊRͷܞĨxįÛjė^¯æķŨqŦŠNũFĖ6ĖTs˜õëæĻdŋ9ŽÆfķZ48Ņb$Sk5g˘Ôõæ|&ęÖbŲŪ”Œgä°Ę#+^Ŧ7ęAR×3 -ČĘņŽn™Mĩ3kSRÍķB|ÅÍüM1ŽņGĩĨ—‘ˆ] …*‰ŒaķPÄ]M$Švŗqk&ęŦyŗLôŨbËnĨžSÔĩ2,A*îĶģĄģPâVīėųl2>īŒ–3itēšPiŦÖģ‹åƒ;’­ēä3ž×{j¨^IĸNßjY㤘Â÷öm*¸ëŖVģ˜2*Ã\¨;NUŅ…ĄTģ82CŖZjÂĮzkB6%S¤3îß?åĶzq’ģ_ĖŌŠÄ/Q&]ŒL-2N[‘™û6[J&=HĖĻKm–^OåÖM>ÍëOˇmgbķų0ŽmJfō šĘ‰…2ĩÁ‚ˇŖL/ŗė­ŌÅĖ­ōíėmFŗW›’ŧ`Œŗą"ęˇ •l>ƒ …r<[Oāۂ\ŸôdÜŽšŧ™]´ļęYQ—PĶ4r‰9[†;Ņ\1Ą<Žz<÷ØQŨ’í!ʍĒļšLsdĸÚOƒģPW ËÎ].#mÜܔŧ3#‚…™}×ĶoaÍž[Fn+…›ÖŨMRO `ĮĻŪϤ‚"CŖ< Ž#ņ™Ņ†ę6žá\'+ Œč(^ #+9wōĨR´×y°ō­dŦŠĖqū)ĪZž]\ ›éûd4Q§q_Î&GÅūüžŨH=ĮgĘŅĖÉ÷7ÉLĒŗXŌÃŦŪÁŠB5›ĨÖ-ô”ÜqŊ€žŦƒnÅPè—BZҍæ‹ŅX­hUī3Ĩ$/N…Î>ē–•:,ˆn>TJˆ"ÁÅAéĄTšw+wĨîĸ<*Õh åÜ{vírHTKít9?ŦšĨ+Û ŗÔõĘOĨZž4+šč}SĒgKËØCļÚHbĖę]+ÖU&cüÆĸų e]ģ.X>TRCû6Ŋ̘ÉĻ('“•áĸÅcŲpEi´U’w{žšĖwÔrQTĢÉŽB*ĸ:LöÔX]T•l_-Ûˇf˛ęŽy-Ö ›Õᐗ‡ s(F‚LīLĩ1ž%‹‡ZJLC1ė>ŊVëÎ"=e\›äŸâ=Áj":OŅPŦž .rũXŠūtWĐ÷4ÕĢΪ̇‡kķ¨/d÷MÜ|xh6Ú éŅsí:\Ĩj᭘ÂVņÎČĒt…cÖ¨¤ãĶļœÜE†‡eđ1Æi>ÚŅ›J_ =.ŗˇÍøm׎Xú(yf™ō,¸Ē$æö0‰ėƒ­"ņĘŊŪĖåĸyV7mÕåÍú öĐ\Íâ}fGZą^b5čŦZÕ^ōv0ė´&Ŗt&1s‘ļuģĖTØ*Õ.IJŊŊm÷ĖÜjĀI[Qā08ëdÍŧ;;”Ø ĶŒŨ[ÃtģƒHa:4ėnbTÉŌcˇŪ“#ÖtĮRw>zhTí^dY™%;^5TŊMŊišf gOũāŌEÅVuEûåû*z¤>ÆĢ7éūíČ6ÍpeP´šĶQĸį"ØĐlGY2¸1;ĨQ!>,´ē#ŗbŗŪ­Ų˜ oBî{/ŽÚ‘QÁŽĖA}4¸ëŖ)Ũô&Š•1.ŪOg)ærÜx{ŠŽnŌ=4oÔÂŖI9´Āãdz2I­réÜl*ģ–?],L+#ĸ×ĒĘô)DÍôŖ=‹öĒuSŗzJÍi#ŧ)9[ĒÚP›7Ÿ’3­‘ģ'{tĶLkˇOtt+ęÁų<ˇzļyŧ=īŪ†ĪVæĸ)Ö šEņ)ēĖTc‹É]ܨ[ˇ›’Ë0MX­bšžĐüŠdˆ†Fj5M°1‹=ļ7…ļkŠ.n‡7ŗG­q;ß&Û"õHÔÛÎĶ ŊĶÛÛÛûøRwq>Ņt\%nV)ãn9”o×bŅU”VĪꞋ Ī ž™h'Ŗ‹\­×žKWĢēYi+ŊaˇB ÎSzIÜx)9hU:ēnëC7ŸWІŗ(Yęáē~o3z4â,$L=Ļëæ\.âĖÅ:šŽąõT]ŸrŨ’fú\‡ôLG_ÍÃU=;ŅŠvg\ø~’ÍčH lŅĀÅp9’5müvׅΚĖ}úëZuāąŠËZö˙û"ŠŋŠļûN¨CyYö]Pī– ä–‡}KˇöēŸEųŨØp7õã`>q.|{…û¨Jä~ëąo…øuļ—;ĶļŪ8_ŧÛOZNYÖ°0¸¯›oŨŧoS>lōB˜Kküö–o’iė'˙OksŧM°Ÿ÷'=¨wūP$kö<`ŽëķËSÔŋīdCųĪt<ÛžkŠeÍ•xĪĢ!Ÿ:˜Mß÷kčũVΜZ¯m/aßë- æØļĻ^ƒ{ Žzk\dZũpk2Ũwžđ=īÉēŧ¤CslN[ƒūŪ%; ޝéŽ+éÛ­žWcƒŠGkú–98Y›ŌVßŪrŒ>?ˆ¸‰āˆPâdÚyđúŸŦėt>ļ>|Íî|ɍ[Vēså!÷ŲëËõ-gĸŊ3kōƒųŽßöĐ{}.žî;ŨĒū,kZhMˇfíķģi9?ÔfSĢņ{:ļļķÄŽ¯.ö[Ŗ™õ6äĨbÂŖbĨC€âÕ7ĨÖ¤UëZĨAĢ‘˛–{-ymČëkøĪöûĐëŽSr=Åũ†ĩđx0ōx°>›ôF{övNŧöˇrāŠIk%ģ&#'`Ģ˙ÁÍzĮ;ņR/1I‡Đ`/Ēęíž/ĄōÃę˗ōĪ/g3˜@o>ŨėüR¨ožđ?ŋ%ZqúōĩįŸõ Ēz~žŦųãc×Z‡VM!ŗ˙dN<Úĩwŋ­đĄƒ!žĄC{<ąŨ˛÷°Ûr0´eß7žØōeØcúp‹˙xųé˙äûĪõŋÅKÛķ%hNŦę`ĐûāđŨŧėpĢgõ'ō&ūļĮūkĒ.Œ¸“ ߉gxs›ƒ¯#]ĩŧör ĻTũį—PØ{:ŗuа>ˆFĄAwÖë…7WbĒîąäāĄäĘ]Ę­>8d÷ĢŪûõß_/?ī7Νo“ôĄŋkdŊ¯I5PˆÆ(ĸ”kHĶhĄ•BšÂ1Qž=Í9aü͜_MėÜį˜^į Æ3ŦŅĪGĪ]C¨d`˙ú ė 2°Ÿķ\ š 0„ą|9TȗɈ?đZ^^^˙<ŧĻx}>ŧf˜–āL™JÂY&QĻ„DjėlvR¸ĘŨœ˛8Ā„ŠTĻ0ŽŠĸ‰~a¤ˆŗŅ‡**áB"?ėˆ:}GÔÁœęú“Õoŗr”ūēĪdV'Gn§"ĘgOū\JėjėO6؃u<]`CÕąüw!H>ËfQES ŒPՙēÄɡũ›JÄäˆKŽS SąĮo6gŸ˛öfæÅĘ4ÁũęãÛ0œô{FkT%ĖIîŖr, Q…3Ėü¸*..îw.¸='ÜräĀ-QšõWØÆšFh@ž^ÁM“ K´ux‚H€1ų)Aˡ.ĸʏ|åœ0F4ĸk˙k[û3 RŠĀ2ū§/ã9§Dą„3ŽIü’PĩöI‘"#ŠTEĒą X•čēŦĘ=ļû”÷čcWOē,Q°D!n‡Ļ(§,āäę%HŅ—K~Õ/ŖŖŖ„Z`€ŅįÃhĢMD%NžTšā×ü‚Ņ000úG`4˜Ņg„hĐÛåŅ œ $Ũ-ޤÎ:đ_ŠSÁ6 ‰TPį|Âßũ$ūKû•qĄ ‰îäûíWĒ‚ũ ö+د Ãū1Ļ˜Ę¨Dd.s6Ŧ_0ZŒŒŒø¯Įh"x€RŽá„Š_0šFFFƒ Ûzˇˇõr'œ‚IÜF”cLą*¤1čĀ­LčĀ ˙ĨÛz•ŖDUš†…ÂësåY€sŽ •rŽ…J.aŋ^UŦŲųf$[lQĐ ~Š1Ē-@Ĩ}‰‘ŗû¯w™ųn Ā-Ā-Ā-,ũ¯+‡ (ŽUĢ!Ė…Š‘æ¸ĨˇˇˇßŸ´Fŧ=#Ūj8 rs…bN)a~[p p pûípKmŋ/e _[ˇš¯U0MūŖ(̐˛æ¯õk!HY.ĒĢLYŗb…/‘hėJ°+/qČĻߝ€ÆˆËqqq/GĪ|w:_ ŽÄÄÄŊ€v €{FĀÅHÃD‰*ÁV¨L[īÛĸX hdč›¨ˆ+xŋ 3I~ڛ†Č–0%ĀŠSJ5.J”/O[ĶŊyį–ĒDî=¯čîģíÉ[“ĩŽxVÖįžŸoۄxĢ­ķjëtÖR)=V*%ę9ĨԓΎôŅ)šĀ,Į1KÃzœš5É.É8˙<ô.!¨hCÅaC6åųŋ.„ÃLÕۜrÃ$˙-8Ŧ˙ÆW…Ã~„QNf”2‚ĩƒö0á€ģW‡ģė_Ā]ãî¯ o5Ė‚Ē\ã*Ō¸"o˙2ŧÕoo}ˇ7ׄˇëŖ8B\¨*Ļ”r'ˆŒ`Đ4ET`¤1.ÖxKI€ĒL#l™Ŗ20‰ŋ„đ€" cLæDÕT ‚e¯,X{+ųÁԜļũ­‹6OŌvõŽcTo´g9ûē–>;Øęá!uŽ ›Sķũ10vęãŅr`\SdtÄ9zã Ø ļĶĻĮÅŌaQMĄr%Hąœ%čF8ģâ‡Dü°`XeLà v‚)¨“E×9F]cDåLr‚ |ŊGBŌ‚4É_õe'ØŨ×fw3°ģÁîū~žk2ŋ†ŽČÁUCL]§ŊëŦīW\Ļ`}˙0ëļĄƒõ}¤c‘iĻŌŖš´ĀAčžRƒĐ 78/ëXŧũ{đ9oÁąø}qšōÃ/§ sU€ŧüŖ/ôôũáË×Â\b+cü„Uʁ°ĸ8Įŋn`T„¯„€0€°¯A8×mü’ā9nY“†ÃzWBSÚŦYŨđx°}ģGŗ;ąn?™éVßúwÜwƒŲA¯ÚËüŒ? “?``Ü2ī÷Ō6JžÂ¤ØĪĨôQ„ÅÂ[dHlŲžŽ3d߯“ÕĩzrĖnCĪĖņØ|ßå^wĀ ”É•"…=Ør/Z/ü?ŋe‹Ršĸ&äbÎ1 ° …:’væhß#ŋ3ލs:‘=ēÎ\TšŽdNWưĸūķKV5 )N•#)Ú˙ũŖWėm¯ī€;§ ÛSxë5Ü…'ļg§ūĀķžOI[ļY_î™CØĮs67]í|ØĄĸh>™æˇīŸŋQ@U^‚(„Ļ2ė—AaFŒ¸ĀŒPvĻ„ŗoÕBáĄR°#åōBÚI[3‚cL Á„€ q‰ ĄøtB`˜0!.0!ČCh>1™4&Ä%&Ä[†aî“ AaBĀ„¸Ā„ Č§‚Á„€ q‰ ąÃÄ'ÎmM â‚!ŸN&LˆKLˆ†`>™Ú_藃áΜO)‚Ô€)q™)ąãŠ`~™ĻL‰‹L ųsJpSĻÄeĻ„OY‚c˜0%.2%4ŋ˛)Sâ2S¯,AaJĀ”¸Č”ā~e SĻÄeĻ„_YB)Sâ"SBė°ņÉ:ŽÂ”€)q™)ĄøtJ€÷ĻÄÅļŅmÍ î“XŪk˜—šÛ4ᗭÖŧ×0%Î6% Ŋ敤×ô ūpzMUģŽôš˜á€@Ą\0U(*e°ÕR ĨN´ž"#ĖWŲ53)0SåĢāBe;įo p×QŽ¸Û”|Vc5ĀUŽ)‚c1ŲˇNúL% IcRö=gĒ&?˙ļ| Gžķ_‰ŊŲDŋ&›¨;VĖŠdÂūNÁ—Ä ģwž¤˜Cēëŋ=b'>ÎĮäĘŌ]S-@1‘ĀŒ& Xĸ#gŽĄĸiō#¤øëdĸ˜"kE…B$뤗Æj@ÜŠ3G„KP˙žt×Gvá‰í‚ōyēkĒÕüõ'+('P žļC|ä:+×S¸Ä;Mâ$fHÚö˜2 ”r@0ķŲ!>Xȅ‚ĸjåQū(ëŦ ¨\yČĻp*Ģ˙§øׇ§6ČÆīg+ ›ŋl0:eaƒŽŒmTǤņ­RŒ9GĒĘ%RRШP™ BâFūŧJø^ļaš@Š ” $NņĪ/MÖYHTgˆ U.1žqasdžØāŸs‚kūzŽĄėxŽQĕq&­rcF]ŖÚQ€0•†6×4ž6lj Ø_\Ŗí,T…Ā7.lŽėÂÛ\ãwŽQk€kN8ŸTáׯ5ęΊHŨJŽøė|RMl;Wņ>6ãų¤Gvá‰íŽŽŽņ;×`|×\Y°ŪoŽŅ€L.ØTæYSu×ŗM8ūâAĩ€ĻĸuĮ„á}oēJŋlŽėÃSlãwļĄĀ6=ÛđȿƏ†P%ĀĄŌöĒМ“~PŠF(sŧHáūŠC[ģŌ‘r †¤Å/‘ĐĻqE`Ļ:āū}Ds\žÖ`™ĢŒ’Ļ pĶßÎMī‡/ū!­ī•mZ’0ŠB*â S,ˆ˛ņƒĉ ŠÆˆDKę¯e$s&BĶTYéĩë]Ґ"[ĄS•īÛt\žÚā'Ÿ¯‚N؏ķŪ–qg¸!E‘6tŒ9ÎD% 2EU]Wĸ"ˆ¸Ā†qīũâÎíŧžjëÎXŨ€?ļ˜s´ũÅ>ąåüŖWOēúÄMé×7ÕŋÚÄüІyO{į˙ü;ž>âÍū¤ĩū§>´1QČŋõA}`Ę?•Ē˙ŗųm*ī1‘–ƒŧ m>vÍéã`ÜC˙įßGiPMkŨŅŋũ–ü^ošã‰5Eŋ^,ĒŦ5ŗū×˙ũŸ˙#¯•ã]ū_˙Ž­Q”íąeõjŨ™%ŋ¯Kũ?˙Z‹ĄŲoX—ōō퇿¸ņīt!×Gō_,ÆųĻrį›Íæ|“ƒR~Ŗt]„ŠuÆÖE´."¯vžŠx]D•üˇžokÜ7§ŗąŲũwTw>˜ļėæÔę?šõé`ŒūįÖ­›Pōëßú#úŁ_ŸāõžyKöwëÉŌåķ\Éî;˙ķË KÛ_æzL^§ŦēSTõF垄]¯:ėNXĨü Ģåxw e×Ļ )ĘV–Ļ9  P…Qę֐ĪļרÛUVŠD ˛kŠ}Ŗŗæ¸.<ĩ=°J9z•˛…ÖžÄŪ?\ķolwŧ}÷:…zû2ĻŨˇ>6PĀYāŖ ô‹-~ĀŦŗŪWfŊ@`փYfũŸļŗžO^ꕙõ;&)Ņå{ÛĻT ÷Ųļy;oœōīŗëėÃS†Ŋī {í<†Ŋæ1ā”Ũ÷ũ)kŠvMˆv°ũõÍÜoLQŧ6:ą“›DŖŽ%ú‰7ŠĪž˛QūXDҁE,ĸ`%QĮoҧ×ļC˙k$ßģˆr’īÕykÕB1˙Æm“Göአ‚E”ßQ'${˙ÜīF{îģQ Á"ęb‹¨tN˜ö_kړĶū$Ķū}_4ņ@[eŋm™ĪĐöšŦşŠ8ĀAīŊãOų[Ū'™ĢËÜáˆčw§ąæQįK:ėÃSz‡ßõHŨdķūx—l”ĢKĩū%QüßK6_C_˜kũ¸><ĩA@6ž'|q]ÕöĮ‹öH+öHÃiˆjēXT“ĢŦ´ÛfČķ3ˇ-*Đũš?6’œŅQÂŊ˜ķōąŊ+’͜Ķāƒų!1§€ˇgÄ[Ío}¤¨€ˇ°›œņ@o"” 1ĶųˆĀ9ĸ…2•1 +#Ėdíēx€’ĻB8(°°°Ā3 @˜*Dd“\Ō!WŽv’ķ+s’;IŊŪØ. ģą@ŲĖõ­>rÁvĖ-vŅũgGöā‰Íšßäœ@4ÖßN4âø|QŅ\:kwãūŅDs\s€hޘhŪ“ĩˆ×pģŧŦåŅZpo€°ÂÖßėŪP@Ų:›{CˆũõŠÜœÂūkĀDĀÄC˜HΉlõ$BJ €D€Äƒ8Ήš—ãōYz¸j?CčųߕūCČ!D›€ĐÛ,˙°đʍųšĐouĒÛk=Å B.æ<˛Ol8}īT Úä¯'šãs˙p ˆæÂDƒąĮ6’ŸK4Į5ˆæŠ‰æŨŨ´h'8 îQƒ‚ (Hߎփ~tÆÜ ڏõØ Øŗ ™blŋl!QĖ9O0ņ ŧŋŧoT pĀÄÃ.L@Epa‚˛ėm—TYWļņk6~ī>FęĩÚ윴|džÚЖũŽ- NĖŋžjøņTC€j.íÄû›†0Ķ× š+&š÷OGhīŦZČęöJ‚ŌJĪAĨG€Îs>õ+ūÄDØ, ˜˜xáøîsbâžKĐAØ- š•€€ŪzAÁ8'((ĶŪ+†+ĶWļķ÷:äŽãs/(Ļ Ņ jĶGöአuÚ÷ę4ėå˛9~/§Đ€l.N6Üg!7'“ ‡˜ ›ĪŊûÔ Û9A≠$Ž7— ×97?ų“đđđđĀ+¨Āāéņiüŋ1:~ŋ—ąW&?1e_:ŲSN˜Īv|yÔ_P~:˛OmČO~—Ÿ0‚cRo0:o0đÍÅųFš6žQ€o€o>Ŗs1mĀ(û#îģu.¯ö‚ĐB]¯ĐE[”.XyŦWė„•Ŋ˛•ĮרFߝÛč+‚ÞnåqdžÚ Xyøå!@éž9ÅŗÂ€o.Î7ėÚø†ßßß\)ߐSÖ7ĘĩåĶû|ŖøŒo”SųFžšNžÁŊø†œ˛žQo.Î7ęĩņ |||sĨ|ÃNYß\ÛFyņųFøŒoÄŠ|#€oŽ”o0đ đÍ)ë|sų,`hŸ•Î7Į5øæšųæŨHeęЌ/ŋ#ßË_‡?Cœ2Ä)˙ĩ§R(Ä)Ÿ/3‹âSĀvŦ\ .ë¸~\€{>Ā~\ € Ļ7˜ŪĀo˜œ ¨?“ãbLāP<@E@ÅCÅ3‚ĸâSP„Ķ“‚"P<#(úķ¤P¯ŧ āŦ‰ĐöėįqÂiģbņæŽÅÃ×ūĀCŌ¨ĪN䤧’FáDÎ+Åcû |CNāøæâ|Cø•ņÍq žšfžy?Kĩ_ČI 'œôírDXŸQŧg~õhÂ!Ȁˇ€ˇ—Ā[°>'ŪúÕY* ‚"Hú4áä]đi‚Æ|Ā4˙°ÆLŽîäŨ¨13Ÿų4ŲŠ3Ÿæ•jːøÆCx˙8ß`ā›ËĮĐđ+ã >Mā›Oå1ņŠO“ĀfJP}@õ9¨úĀ!´įLîäS×#Í”ŠŠAŌnœ}ę$ @@@ņ ōn€ôÚöķĮõÚĢ;ĪķęĩŠĪüƒĘŠz­ūÁëÔk ėyž!'ėy$*đÍåãQø•ņ ˙ đÍgTÕ¯ūA TP}@õ9 úĐ|Χ„Ģ~uÂÎD8Šxxā•8đĀyQØ1 ˜˜xØ# IˇÁ# õ%Çjzm;&Õ¨PĢ>ķˆĒ§*Ô*xD¯TĄ†“Ā7ä„“ß\>‡_ß(āžųŒęŖųÔ#JaĮ$¨> úR}āäŸ3*ášO=ĸ6L&&ÂDČxNLôŠw2ˆ(āā7Q8É<ĸ PX2|\Ąžļ=ĸÚT¨5ŸyDĩSj <ĸ׊PSØ# |COØ#JUā›ËGāđ+ã<ĸĀ7ŸQē¸_=ĸ°GTP}Š>ėŒę?÷ĢG”&&&ĀDØtNLôĢGT€G<ĸĀĀo<ĸpÖ0xDAĄ>°dø°BÍŽm(˙ 5÷™G”ŸĒPsđˆ^ŠB {Doč {Džš|ŋ2žŅĀ# |ķĨKøÔ#Ę`(¨> úR} ūŒęŋđŠG”ÁQĀDĀÄC˜:ø91ҧQ{DÁ# <<đÖ# G΃GęK†+Ô×ļGTü@…ZøĖ#*NU¨xD¯SĄf°Gø†°G”ŠĀ7—ĀáWÆ7<ĸĀ7ŸQēœ•ÚֈQ|ĸūÃQP}@õ9¨ú€æs>õ#ş˜{Da"L<'&î¸DŸxDáLMĀDĀÄÃŪA@Eđ‚Z{Ā|ū°ZĢ\Û~I9÷ųÜįj-Ft]vAĩöČ><ĩA Öú_­…ũ’Ā7ė„ũ’ žš|4ŠĪŧƒ'ķī đͧTŒÔ€ 3*\ˆRúΝß* P•¤ÅXcÎR SJBš "L^đĪ/F•Æc†9“ë`ÁŋZúB"Ģā¤WUUķæ1ûĐ5Bvķžf|đA°úņx€Ę(ú•âõ€[Á;đŲöltØ$đhûļWˆXR‚$„¸ēĪ'7}8"ÚFņ{—É .ˇÛFØs5ļ-ĘĶeQŊÛ˛ûŪ¸ŋ'JüH!ÕųÚ|ŒÖĸƒÚęŋõ‰ķ‚ū­ŖÍ˙ß'ļTZUä(­ä—4ÍŋWiŊëĘQŧkøŸQh˙ų…Ö”.ÍË]Âū˛Ü ‚Hãķ7<€¨´(V…ʤ5ą~°†L¨HÚĨŒcĒhŌ–p,âP$UTÂCl‰Ķm‰ƒĢiũÉ掯Y9^~ŨÆgÖÔäHC„ Īúō–žûĶŦ—ãÉíĻČąžÂ š¯ÎŽž -EãĸjÁ'ō/ŠrpĄÅh`Ŋ$ÃD•÷`˛$€ŖßZXhÁBËG ­ŖVW¯nwĢ+X]}ĪęĒܚö­ÉÄO+,rūÖ[å}}÷Ø¸ÕØfá7#úĨÄŊœōvoíëŌŗ=;—ė"ĪžõâŲ #^ļ‚9Ž7eķˇ'ž95ŨŸ+ũāÄ|ČĸL8\ԍD8¨cÁų°’ŒWģõ~vX#JuS2‹.+¤9lôJËR(j4XIÆDËė•ڍxæ^×cąMÉA”a¤îIIŠ–ķÁz/;0äũĻ‘D¨”_ֈ@›’ļë…æķ‡xžuK÷ųYŖŧ˜`å =ifŠŅ|ĩž)YčfÃ5‚į˛>ŨZ?ŋ*•BąĶ=D”‚AĤF“…MÉz,ÚŽ–¸éČŋĐŌĒĶį›ŋU:nÛÉĸY§Ųd&¤ĪaŨ΄Øâží|_˙>Ëēą)ŠWô­?bÁH5֜ԝ§ßŗá˛S lVH)š)™Oča[7Œp‡įÃ[×õEĸëÎjķ„ĄëÁŠŦûrSŌŠ}"ÚŦ–[z|eë!]īčA[_ČRÍ ÍÎ ĨŦ۟%Ũų &ķŌ]Įɸaë{_›’bZŅuÃų=\H›F"t¨¤ū¯’ÁhģO"į§âúŲNõ!Ûv~ߔl:ŸÆ´ŽKņõUų–I×ŋ‡‚¯÷ æ—åh×t>mJõŸOģWmJ>jl>íÔĘJĮų}Ũ'ņ ü=ßyŠghá|šh’FšÔYwÂú÷*1Ë%ęüž)‰OCųIŖŒ‡Î§7›{OĢåčJū ŋÖSŽķj9;p>ÍŦ;aą,E“NČs}ŅwßŅö§­ßQõĄ‰Ūôg$ûTw—fš1h„ôAeŲą3…Ž’ ՇÆdS˛úGé’3ēĻÍzΰå(žĘ<ŗdgTcĨUežķŽî×ŖŖĩ)DŪd[h™nG<ßfrĩ‘nÔXUšB…¤ gĖ?B<žœ÷—Ø.y—ÍË،¨ÉĸR. ėw!Ī•~rĘæîœņ—3^GHâš@,U•ũM›˛ąėđåŪ¯÷LϜoäãCdEōŨ:Éŧ>ĒĶnJËā ĪĪs-ūÔ  šîuģ•ĨŌ6=ëé\÷Ū_͟ÁįŲöļdHYŖBļQ˛íĘ4Ķb‹D$ß­öĸ¸&qÂyے‰XöŠ¸Ū ŽÖ(x¯Oņė}%ŧžū‡ûžA°?Ü÷ũļ?â­’“b¯ÔĢ>$ģĩ^ļ›ˆV‡rF kŊē3ūgÕXםGfYĖYŊFđ~‹ŌҤŦ}iY-+˛ŽÁ˛ƒfYé'äLŦõōÃMÉFŦûTk‡ĩsčc>îÔˇūļd0ךĩõâÒIárčŪM%ĖD"ķ‰ŽãĨõĻdéÍXĖ“%;¸nKiqÛ¯•KÍúî|Ŧŀŋ– ÕIˇŋyÁpąkØZZ6^æœņæžōyr~p=*_dzbŽôŌ3ļNõđÄ-iȉĢGza.‰ ÚŅė`[ŲzÍ.õx]”PīŪ3QŅ[s ŪÉĸŪŗCzĘĐGFhϧútâz&âŪsa‡ĶzVvˆŽé9]WŒđX—ā)ė°Ļ‘`pÉē÷Ėg‚1#ō¨ßƒŠydŠėāęEY;ZÕËĪ|TGgúC1ø¨ĮÂzeėØ1S7#Á‘[čĩNpnÄ]ÖÖ‘ 1âMŨęšW Ŧ'Šz…’vâIoÛî<2ŒdZīĄ#Ų×û‘e§"ú€…zzĒĨæĄŠ~nûdÂFÚÖg“0$ĘĖ'᨝ięKÎéذޏËGe;ÛĶi"ühįRēR ėÜL×áÅüŽ ßč˛î”MÉ ^‘ãưƒ!ÉÍķ‰`´ŠĖķ‹`œEÚúŊLŲ‘MÉŠ]ˆŗ™3ä[ēcҰQŦī'Ҝ]JK˛›æ%ęŪŗR‰vír/X+FÆC)hŖÂŽÄ$īĒķ uēÍ­§Qû“X[7ÁÍõZ>8ËĄQxj^î=WŒ ˛JŧgX“ ÆãČ~ėoíDhn?†Â‰„k/ŲM3c Ûn•CŠAâÉn߇rõÄÍŧc„î‹ÉœŨŊ۔ •¤eôîBĻ‘œŲũģULŪĖųPĢžē3†ÅPor­JcT XjaŒëĄ§L:4Ÿ´BĢNēhL‡!ϧ{úlîŪ“Ûif<)á`"“˛áptžiË\8UĪĖõU5|—qG]Ø@=i!gË6Fá ˎ 7XöFg…p‹åî ÅĩCÂ}žëÍUž&ršÍ“áeņÎ0„σģŽ}ŗ sîļH ęÉH¨b‹`3gFoR"Ųē!ō‘‚ü/_XD§›’ÎK5ãɈe߉^¤Sŧ¯S‘Č(s?_Htšg\ë7ąČ†#ˇėEŽ᳒7bŅPŊxŸĪĸ‰Iqœ/ä6%Ŗwz)Ō,ĸhŠR˛‚åZ´†J,X G[•ō}ž:‰xySrŌŦ•ŖOiĨ7‚QR|hå­YT$ÄÂŽÅ"zĨšoš=Kņ ÎˇÕØŊ^ÍįģãX5R.ú˜m˜Šæ0ë×ÍMÉ~sÍæĩhpĸÅHĻÖjΖ11¨ëÍų(MÔ­Å˛Ŋ)ĪČa•GVŧXoԃ¤¯gZ•ãŨ2›j1>fÖϤšį…øŠ=šų›b\ãjK/%"ģ UÃæĄˆģšHífã>ÖLÔYķf™č'ēŖŨJ=%ύjeX‚TܧwCwĄÄ­Ū‰/ķŲd|Ū- fŌčtsĄŌ Y­wËw%[uÉ/f<9Ž÷ÔPŊ’DžÕ˛ÆI1„īíÛT<2p×G­v1eT†šPwœĒĸ! BŠvqd†FĩԄõքlJĻHgÜŋʧõâ$wŋ˜ĨS‰ ^ĸLē™6Zdœļ"3÷mļ”Lz˜M—Ú,Ŋ4žĘ­›|š×ŸnÛ:ÎÄæķa8\۔Ėär• ejƒo'F™^fŲ[Ĩ‹™9ZåÛŲیf¯6%yÁgcEÔo*Ų| åxļžĀˇš>é'ȸ]sy3ģ4h%lÕŗĸ.ĄĻiäsļ wĸšbBy\õxîąŖē%ÛC”'Tm5™æČDĩŸwĄ:¯–ģ\F$Ú¸š)ygF 3ûŽ§ß Âš}ˇŒÜV 7­ģ›¤ž@ÁŽ!MŊMIE†FyGâ3Ŗ Õ;)l<5šNV<ŅQ>ūŧ:FVrîäKĨh¯ķ`å[ÉX™ãüS0ž-6´<ē¸,6Ķ÷ÉhŖNãžœMŽŠũų}ģ‘z,Ž#Δ+ĸ™;’īo’™Tgą,¤‡YŊƒS…j6K#Ŧ[č)š)âz=#XŨ6ŠĄ†Q/…´ĸQÍŖąZŅĒŪgJI^œ4 œ}t-+uXŨ|¨”E‚‹ƒŌCŠ4īVîJŨEyTĒŅĘ?¸÷ė>Ú吨<–Úér~X5K=VļfŠ;ꕟJĩ|iVrŅûĻTĪ––ą‡lĩ‘Ę=ÔģVŦĢLÆø1ŒEķAËēv]°|¨¤†öm,zW1“MQN&+ÃE‹Į˛áŠŌhĢ$īö|5™ī¨åĸ¨V“]…TDu˜ėŠąē¨*ŲžZļoÍdÕķZŦ6ĢÃ!/æPŒ™Ū™jc|Kĩ”˜†bØ}z­ÖEzʸ6É?Å{‚ÕDtžĸĄX=\äúąRũ1莠īiĒWŸGW9ÖæQ=^Č4ųđĐl´ŌŖ5æÚu¸J'ÔÎ[1…­â‘Ué* ÆŦQI Ƨm‹'š‹ Ëāc.*ŒŌ|´Ŗ7•žz\fo›ņÛŽąôQ"ōĖ2åYpUIĖía*$Ų[Eâ•{Ŋ™ËEķŦÚŊȲ2Kv:Ŋj¨z›zĶrÍΞúÁĨ‹Š­ęŠöË÷ 4TôūH}ŒWoŌũۑmšáĘ h5§ŖDĪE°ĄŲޞdpcvJŖB|XhuGfÅfŊ[ŗ1Ū„Ü÷^ĩ#Ŗ‚5™ƒúhp3ÖGS<ēéMR+c\ŧŸÎRĖå¸ņ(öŨ¤'zhŪ¨…G“rhĮÉôd’ZåŌšŲ4Tv-˙~ēX˜VFD¯U•éSˆšéG{í1TëĻfõ”šĶFxSrļTĩĄ6o>%g"Z#wOöčĻ™ÖnŸččVԃķynõl+ōx{ŪŊ ž­ĖE!RŦr‹âSt™ŠÆ“ģ¸Qˇn7%—ašxĘtÔe­ŸĘeFtšĒĨ§“9^Ĩ˛Ų:ÁĢvÍeØé„Ķ•Ö6rŠ ĢüĶ4q‹&ņB>{Ñ~q)JnI܈—KZãÕË6;$׎ֲũé×Ė1“Æ÷Ãķ;š"ƒVkV<Ģļč˛˙8žŅ KĶf>ΰî]‹Üd]æb7OúM+•B/lÉĩŽ\EŒ,{ŠĻWÃüŦo¨Ũ§‘‹ŸÖtŽŪ>Mš3”×ĒĢY*§" …ŸVwÁ Ī=,ęOqˇí|°ZÅs}. ųSÉ Ôkš`c{l n m×R]ÜnfZãvŋMļEꑨˇ§z'ώˇˇ÷ņĨîâ|ĸ;č¸JÜŦRÆŨr(ߎÅĸĢ:)­žÕ=ž<3ŅNFš"Z¯=—ŽW)tŗ.ŌVzÃn…œ§ô’¸ņRrĐĒttŨÖ#†n>¯  gQ˛ÔÃuũŪfôhÄYH˜zL×Íš\ř‹urcëŠē>1äē%Íôš陎žš‡ĢzvĸS#ėθđũ$›Ņ‘.Ųĸ‹ár$kÚø)ėŽ 5™ûô×ĩęĀc—ĩė˙ū÷Em÷O‡6mėģŪ-9ū@É-ŋú–ní!u?‹ōģ “ÜX’Á|â\øö ÷Q•ČũÖcß ņë­ wĻmŊqžYŲé|0l}øšŨų’ˇŦūtįĘCîŗ×1–ë[ÎD{gÖäķˇíĄ÷ú\|&ÜwēUũ >XִКnÍZ÷}Ė7>ČmŒp û­ŅĖzõņZäQĄŌ! ņę“RkŌĒu­Ō ÕHYËŊŧ6āĨû_į×ĻI=s<]÷—ãƌÍZ kĸ×§otŪøzą3Ė~ŨúäĩʼnéO[ã­×ųú2]?ŋ1˜ŋú/1e­×¯åĨí/ũĩBs§4~ m[WeŗO…9 J8Ųņģž°Ã Ŋ"ØËßķŗŽ5>X@vPÍü͟ˇƒ^ß>ķh{É˙W0kûW ũōîpÉ[öÛi÷6ԌŠj€žũúį×oŽY€j›đ2Žđ­ŋ;›Ė˜ĻķĄGįŊžĘ˙/ҟ -ĮŋUë7¨˙ė‹ôƒ^ŋeŗa 'ŠÍĮĮŽh=ß!ļūĶ Ö–ŋm=ō•DÖO¯Ŋ’ŲŊ™Woø–W_.Qaú{lö×ŗq u]xS•íD[Ņ'ûNõ-fûã#÷Ŧąmy?XÛi/øinõëŨŲDŽˆßÃņ`đčũLî¤ÕøŌgníũäŊ$§vs"!Ëęחīļo7W;튍–ķCm6ĩŋ§cë@S™ļũĐ_Ŧ'}ÁŗŪPČ ĢŦ-9€Įōy9ĮFęŧáŖ s‹ —Ō\éXDĪĄēˇˇņŋ.Ÿ>ĩîeÕģÖ]8úļ 'Ĩo6ü.†IiōR¯ōė@ycfv[ĶåÛK\#āÕ xSÜ ūÜĒøkįė1¯Ûųo p›ƒ[]+:K ÖĢÆh¯ÆÍi¯ģîēÂrh}ė’õtkXAŗŪqā´˙Ė{đ%šdũĩŲ%ëŊY~Y]s2Ŧ;įPĩ´ŊjIûAšēkÆÛëųˇæãf-h™\ŋģ<\9øŨ™cRĘî fîößæîߔ%ĸŸ*/dԟē yûTųģėįĘŅO´@šĨĶÉĮĪÄ|˛ôI´kNŖr¤æ†Îåģr6ą^ÖĨ‡_›,•Y„w‹dSoŦxĄ˙Üģe˙÷˙ü˙úŅJŨ īgolang-github-transparency-dev-merkle-0.0.2/docs/images/compact_ranges.svg000066400000000000000000000720041455123105200267040ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 compact-ranges Layer 1 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.1 2.2 2.3 3.0 3.1 4.0 1.8 16 17 1.9 18 19 20 2.4 3.2 5.0 golang-github-transparency-dev-merkle-0.0.2/docs/images/compact_ranges_merge.svg000066400000000000000000000772171455123105200300760ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 compact-ranges-merge Layer 1 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.1 2.2 2.3 3.0 3.1 4.0 1.8 16 17 1.9 18 19 20 2.4 3.2 5.0 golang-github-transparency-dev-merkle-0.0.2/docs/images/consistency_proof.svg000066400000000000000000000527631455123105200274770ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 consistency-proof Layer 1 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.1 2.2 2.3 3.0 3.1 4.0 golang-github-transparency-dev-merkle-0.0.2/docs/images/data_model.svg000066400000000000000000001654761455123105200260300ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 merkle-tree Layer 1 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.1 2.2 2.3 3.0 3.1 4.0 5.0 1.8 16 17 1.9 18 19 1.10 20 21 1.11 22 23 1.12 24 25 1.13 26 27 1.14 28 29 1.15 30 31 2.4 2.5 2.6 2.7 3.2 3.3 4.1 Entries Level 0 Level 1 Level 2 Level 3 Level 4 Level 5 golang-github-transparency-dev-merkle-0.0.2/docs/images/distributed_tree.svg000066400000000000000000001234271455123105200272660ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 distributed-tree Layer 1 Worker 0 Coordinator Worker 2 Worker 1 Worker 3 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.2 2.3 3.1 16 17 1.9 18 19 1.10 20 21 1.11 22 23 2.5 1.12 24 25 1.13 26 27 2.6 28 29 1.15 30 31 1.2 1.3 2.0 2.1 3.0 3.1 4.0 1.8 16 17 1.9 2.4 3.2 5.0 2.5 2.6 3.3 1.14 28 29 1.15 2.7 4.1 Compact ranges golang-github-transparency-dev-merkle-0.0.2/docs/images/inclusion_proof.svg000066400000000000000000000533611455123105200271340ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 inclusion-proof Layer 1 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.1 2.2 2.3 3.0 3.1 4.0 golang-github-transparency-dev-merkle-0.0.2/docs/images/inclusion_proof_range.svg000066400000000000000000000631641455123105200303120ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 inclusion-proof-range Layer 1 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.1 2.2 2.3 3.0 3.1 4.0 golang-github-transparency-dev-merkle-0.0.2/docs/images/witness.svg000066400000000000000000001404431455123105200254160ustar00rootroot00000000000000 Produced by OmniGraffle 7.12.1 2022-05-09 21:38:57 +0000 witness Layer 1 Witness Log 1.0 0 1 1.1 2 3 1.2 4 5 1.3 6 7 1.4 8 9 1.5 10 11 1.6 12 13 1.7 14 15 2.0 2.2 2.3 3.1 16 17 1.9 18 19 1.10 20 21 1.11 22 23 2.5 24 2.1 3.0 4.0 1.8 2.4 3.2 5.0 4.1 Old entries Update 1 + = Update 2 + = Update 1: Update 2: 12 2.2 3.0 13 1.7 20 2.4 20 2.4 4.0 20 2.4 4.0 21 1.11 3.2 4.0 golang-github-transparency-dev-merkle-0.0.2/go.mod000066400000000000000000000001341455123105200221020ustar00rootroot00000000000000module github.com/transparency-dev/merkle go 1.19 require github.com/google/go-cmp v0.5.9 golang-github-transparency-dev-merkle-0.0.2/go.sum000066400000000000000000000002471455123105200221340ustar00rootroot00000000000000github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= golang-github-transparency-dev-merkle-0.0.2/hasher.go000066400000000000000000000024741455123105200226060ustar00rootroot00000000000000// Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package merkle provides Merkle tree interfaces and implementation. package merkle // TODO(pavelkalinnikov): Remove this root package. The only interface provided // here does not have to exist, and can be [re-]defined on the user side, such // as in compact or proof package. // LogHasher provides the hash functions needed to compute dense merkle trees. type LogHasher interface { // EmptyRoot supports returning a special case for the root of an empty tree. EmptyRoot() []byte // HashLeaf computes the hash of a leaf that exists. HashLeaf(leaf []byte) []byte // HashChildren computes interior nodes. HashChildren(l, r []byte) []byte // Size returns the number of bytes the Hash* functions will return. Size() int } golang-github-transparency-dev-merkle-0.0.2/proof/000077500000000000000000000000001455123105200221235ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/proof/proof.go000066400000000000000000000164671455123105200236150ustar00rootroot00000000000000// Copyright 2022 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package proof contains helpers for constructing log Merkle tree proofs. package proof import ( "fmt" "math/bits" "github.com/transparency-dev/merkle/compact" ) // Nodes contains information on how to construct a log Merkle tree proof. It // supports any proof that has at most one ephemeral node, such as inclusion // and consistency proofs defined in RFC 6962. type Nodes struct { // IDs contains the IDs of non-ephemeral nodes sufficient to build the proof. // If an ephemeral node is needed for a proof, it can be recomputed based on // a subset of nodes in this list. IDs []compact.NodeID // begin is the beginning index (inclusive) into the IDs[begin:end] subslice // of the nodes which will be used to re-create the ephemeral node. begin int // end is the ending (exclusive) index into the IDs[begin:end] subslice of // the nodes which will be used to re-create the ephemeral node. end int // ephem is the ID of the ephemeral node in the proof. This node is a common // ancestor of all nodes in IDs[begin:end]. It is the node that otherwise // would have been used in the proof if the tree was perfect. ephem compact.NodeID } // Inclusion returns the information on how to fetch and construct an inclusion // proof for the given leaf index in a log Merkle tree of the given size. It // requires 0 <= index < size. func Inclusion(index, size uint64) (Nodes, error) { if index >= size { return Nodes{}, fmt.Errorf("index %d out of bounds for tree size %d", index, size) } return nodes(index, 0, size).skipFirst(), nil } // Consistency returns the information on how to fetch and construct a // consistency proof between the two given tree sizes of a log Merkle tree. It // requires 0 <= size1 <= size2. func Consistency(size1, size2 uint64) (Nodes, error) { if size1 > size2 { return Nodes{}, fmt.Errorf("tree size %d > %d", size1, size2) } if size1 == size2 || size1 == 0 { return Nodes{IDs: []compact.NodeID{}}, nil } // Find the root of the biggest perfect subtree that ends at size1. level := uint(bits.TrailingZeros64(size1)) index := (size1 - 1) >> level // The consistency proof consists of this node (except if size1 is a power of // two, in which case adding this node would be redundant because the client // is assumed to know it from a checkpoint), and nodes of the inclusion proof // into this node in the tree of size2. p := nodes(index, level, size2) // Handle the case when size1 is a power of 2. if index == 0 { return p.skipFirst(), nil } return p, nil } // nodes returns the node IDs necessary to prove that the (level, index) node // is included in the Merkle tree of the given size. func nodes(index uint64, level uint, size uint64) Nodes { // Compute the `fork` node, where the path from root to (level, index) node // diverges from the path to (0, size). // // The sibling of this node is the ephemeral node which represents a subtree // that is not complete in the tree of the given size. To compute the hash // of the ephemeral node, we need all the non-ephemeral nodes that cover the // same range of leaves. // // The `inner` variable is how many layers up from (level, index) the `fork` // and the ephemeral nodes are. inner := bits.Len64(index^(size>>level)) - 1 fork := compact.NewNodeID(level+uint(inner), index>>inner) begin, end := fork.Coverage() left := compact.RangeSize(0, begin) right := compact.RangeSize(end, size) node := compact.NewNodeID(level, index) // Pre-allocate the exact number of nodes for the proof, in order: // - The seed node for which we are building the proof. // - The `inner` nodes at each level up to the fork node. // - The `right` nodes, comprising the ephemeral node. // - The `left` nodes, completing the coverage of the whole [0, size) range. nodes := append(make([]compact.NodeID, 0, 1+inner+right+left), node) // The first portion of the proof consists of the siblings for nodes of the // path going up to the level at which the ephemeral node appears. for ; node.Level < fork.Level; node = node.Parent() { nodes = append(nodes, node.Sibling()) } // This portion of the proof covers the range [begin, end) under it. The // ranges to the left and to the right from it remain to be covered. // Add all the nodes (potentially none) that cover the right range, and // represent the ephemeral node. Reverse them so that the Rehash method can // process hashes in the convenient order, from lower to upper levels. len1 := len(nodes) nodes = compact.RangeNodes(end, size, nodes) reverse(nodes[len(nodes)-right:]) len2 := len(nodes) // Add the nodes that cover the left range, ordered increasingly by level. nodes = compact.RangeNodes(0, begin, nodes) reverse(nodes[len(nodes)-left:]) // nodes[len1:len2] contains the nodes representing the ephemeral node. If // it's empty, make it zero. Note that it can also contain a single node. // Depending on the preference of the layer above, it may or may not be // considered ephemeral. if len1 >= len2 { len1, len2 = 0, 0 } return Nodes{IDs: nodes, begin: len1, end: len2, ephem: fork.Sibling()} } // Ephem returns the ephemeral node, and indices begin and end, such that // IDs[begin:end] slice contains the child nodes of the ephemeral node. // // The list is empty iff there are no ephemeral nodes in the proof. Some // examples of when this can happen: a proof in a perfect tree; an inclusion // proof for a leaf in a perfect subtree at the right edge of the tree. func (n Nodes) Ephem() (compact.NodeID, int, int) { return n.ephem, n.begin, n.end } // Rehash computes the proof based on the slice of node hashes corresponding to // their IDs in the n.IDs field. The slices must be of the same length. The hc // parameter computes a node's hash based on hashes of its children. // // Warning: The passed-in slice of hashes can be modified in-place. func (n Nodes) Rehash(h [][]byte, hc func(left, right []byte) []byte) ([][]byte, error) { if got, want := len(h), len(n.IDs); got != want { return nil, fmt.Errorf("got %d hashes but expected %d", got, want) } cursor := 0 // Scan the list of node hashes, and store the rehashed list in-place. // Invariant: cursor <= i, and h[:cursor] contains all the hashes of the // rehashed list after scanning h up to index i-1. for i, ln := 0, len(h); i < ln; i, cursor = i+1, cursor+1 { hash := h[i] if i >= n.begin && i < n.end { // Scan the block of node hashes that need rehashing. for i++; i < n.end; i++ { hash = hc(h[i], hash) } i-- } h[cursor] = hash } return h[:cursor], nil } func (n Nodes) skipFirst() Nodes { n.IDs = n.IDs[1:] // Fixup the indices into the IDs slice. if n.begin < n.end { n.begin-- n.end-- } return n } func reverse(ids []compact.NodeID) { for i, j := 0, len(ids)-1; i < j; i, j = i+1, j-1 { ids[i], ids[j] = ids[j], ids[i] } } golang-github-transparency-dev-merkle-0.0.2/proof/proof_test.go000066400000000000000000000302101455123105200246320ustar00rootroot00000000000000// Copyright 2022 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package proof import ( "fmt" "testing" "github.com/google/go-cmp/cmp" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" ) // TestInclusion contains inclusion proof tests. For reference, consider the // following example of a tree from RFC 6962: // // hash <== Level 3 // / \ // / \ // / \ // / \ // / \ // k l <== Level 2 // / \ / \ // / \ / \ // / \ / \ // g h i [ ] <== Level 1 // / \ / \ / \ / // a b c d e f j <== Level 0 // | | | | | | | // d0 d1 d2 d3 d4 d5 d6 // // Our storage node layers are always populated from the bottom up, hence the // gap at level 1, index 3 in the above picture. func TestInclusion(t *testing.T) { id := compact.NewNodeID nodes := func(ids ...compact.NodeID) Nodes { return Nodes{IDs: ids} } rehash := func(begin, end int, ids ...compact.NodeID) Nodes { return Nodes{IDs: ids, begin: begin, end: end} } for _, tc := range []struct { size uint64 // The requested past tree size. index uint64 // Leaf index in the requested tree. want Nodes wantErr bool }{ // Errors. {size: 0, index: 0, wantErr: true}, {size: 0, index: 1, wantErr: true}, {size: 1, index: 2, wantErr: true}, {size: 0, index: 3, wantErr: true}, {size: 7, index: 8, wantErr: true}, // Small trees. {size: 1, index: 0, want: Nodes{IDs: []compact.NodeID{}}}, {size: 2, index: 0, want: nodes(id(0, 1))}, // b {size: 2, index: 1, want: nodes(id(0, 0))}, // a {size: 3, index: 1, want: rehash(1, 2, id(0, 0), id(0, 2))}, // a c // Tree of size 7. {size: 7, index: 0, want: rehash(2, 4, // l=hash(i,j) id(0, 1), id(1, 1), id(0, 6), id(1, 2))}, // b h j i {size: 7, index: 1, want: rehash(2, 4, // l=hash(i,j) id(0, 0), id(1, 1), id(0, 6), id(1, 2))}, // a h j i {size: 7, index: 2, want: rehash(2, 4, // l=hash(i,j) id(0, 3), id(1, 0), id(0, 6), id(1, 2))}, // d g j i {size: 7, index: 3, want: rehash(2, 4, // l=hash(i,j) id(0, 2), id(1, 0), id(0, 6), id(1, 2))}, // c g j i {size: 7, index: 4, want: rehash(1, 2, id(0, 5), id(0, 6), id(2, 0))}, // f j k {size: 7, index: 5, want: rehash(1, 2, id(0, 4), id(0, 6), id(2, 0))}, // e j k {size: 7, index: 6, want: nodes(id(1, 2), id(2, 0))}, // i k // Smaller trees within a bigger stored tree. {size: 4, index: 2, want: nodes(id(0, 3), id(1, 0))}, // d g {size: 5, index: 3, want: rehash(2, 3, id(0, 2), id(1, 0), id(0, 4))}, // c g e {size: 6, index: 3, want: rehash(2, 3, id(0, 2), id(1, 0), id(1, 2))}, // c g i {size: 6, index: 4, want: nodes(id(0, 5), id(2, 0))}, // f k {size: 7, index: 1, want: rehash(2, 4, // l=hash(i,j) id(0, 0), id(1, 1), id(0, 6), id(1, 2))}, // a h j i {size: 7, index: 3, want: rehash(2, 4, // l=hash(i,j) id(0, 2), id(1, 0), id(0, 6), id(1, 2))}, // c g j i // Some rehashes in the middle of the returned list. {size: 15, index: 10, want: rehash(2, 4, id(0, 11), id(1, 4), id(0, 14), id(1, 6), id(3, 0), )}, {size: 31, index: 24, want: rehash(2, 4, id(0, 25), id(1, 13), id(0, 30), id(1, 14), id(3, 2), id(4, 0), )}, {size: 95, index: 81, want: rehash(3, 6, id(0, 80), id(1, 41), id(2, 21), id(0, 94), id(1, 46), id(2, 22), id(4, 4), id(6, 0), )}, } { t.Run(fmt.Sprintf("%d:%d", tc.size, tc.index), func(t *testing.T) { proof, err := Inclusion(tc.index, tc.size) if tc.wantErr { if err == nil { t.Fatal("accepted bad params") } return } else if err != nil { t.Fatalf("Inclusion: %v", err) } // Ignore the ephemeral node, it is tested separately. proof.ephem = compact.NodeID{} if diff := cmp.Diff(tc.want, proof, cmp.AllowUnexported(Nodes{})); diff != "" { t.Errorf("paths mismatch:\n%v", diff) } }) } } // TestConsistency contains consistency proof tests. For reference, consider // the following example: // // hash5 hash7 // / \ / \ // / \ / \ // / \ / \ // / \ / \ // / \ / \ // k [ ] --> k l // / \ / / \ / \ // / \ / / \ / \ // / \ / / \ / \ // g h [ ] g h i [ ] // / \ / \ / / \ / \ / \ / // a b c d e a b c d e f j // | | | | | | | | | | | | // d0 d1 d2 d3 d4 d0 d1 d2 d3 d4 d5 d6 // // The consistency proof between tree size 5 and 7 consists of nodes e, f, j, // and k. The node j is taken instead of its missing parent. func TestConsistency(t *testing.T) { id := compact.NewNodeID nodes := func(ids ...compact.NodeID) Nodes { return Nodes{IDs: ids} } rehash := func(begin, end int, ids ...compact.NodeID) Nodes { return Nodes{IDs: ids, begin: begin, end: end} } for _, tc := range []struct { size1 uint64 // The smaller of the two tree sizes. size2 uint64 // The bigger of the two tree sizes. want Nodes wantErr bool }{ // Errors. {size1: 5, size2: 0, wantErr: true}, {size1: 9, size2: 8, wantErr: true}, {size1: 1, size2: 2, want: nodes(id(0, 1))}, // b {size1: 1, size2: 4, want: nodes(id(0, 1), id(1, 1))}, // b h {size1: 1, size2: 6, want: rehash(2, 3, id(0, 1), id(1, 1), id(1, 2))}, // b h i {size1: 2, size2: 3, want: rehash(0, 1, id(0, 2))}, // c {size1: 2, size2: 8, want: nodes(id(1, 1), id(2, 1))}, // h l {size1: 3, size2: 7, want: rehash(3, 5, // l=hash(i,j) id(0, 2), id(0, 3), id(1, 0), id(0, 6), id(1, 2))}, // c d g j i {size1: 4, size2: 7, want: rehash(0, 2, // l=hash(i,j) id(0, 6), id(1, 2))}, // j i {size1: 5, size2: 7, want: rehash(2, 3, id(0, 4), id(0, 5), id(0, 6), id(2, 0))}, // e f j k {size1: 6, size2: 7, want: rehash(1, 2, id(1, 2), id(0, 6), id(2, 0))}, // i j k {size1: 7, size2: 8, want: nodes( id(0, 6), id(0, 7), id(1, 2), id(2, 0))}, // j leaf#7 i k // Same tree size. {size1: 1, size2: 1, want: Nodes{IDs: []compact.NodeID{}}}, {size1: 2, size2: 2, want: Nodes{IDs: []compact.NodeID{}}}, {size1: 3, size2: 3, want: Nodes{IDs: []compact.NodeID{}}}, {size1: 4, size2: 4, want: Nodes{IDs: []compact.NodeID{}}}, {size1: 5, size2: 5, want: Nodes{IDs: []compact.NodeID{}}}, {size1: 7, size2: 7, want: Nodes{IDs: []compact.NodeID{}}}, {size1: 8, size2: 8, want: Nodes{IDs: []compact.NodeID{}}}, // Smaller trees within a bigger stored tree. {size1: 2, size2: 4, want: nodes(id(1, 1))}, // h {size1: 3, size2: 5, want: rehash(3, 4, id(0, 2), id(0, 3), id(1, 0), id(0, 4))}, // c d g e {size1: 3, size2: 6, want: rehash(3, 4, id(0, 2), id(0, 3), id(1, 0), id(1, 2))}, // c d g i {size1: 4, size2: 6, want: rehash(0, 1, id(1, 2))}, // i {size1: 1, size2: 7, want: rehash(2, 4, // l=hash(i,j) id(0, 1), id(1, 1), id(0, 6), id(1, 2))}, // b h j i // Some rehashes in the middle of the returned list. {size1: 10, size2: 15, want: rehash(2, 4, id(1, 4), id(1, 5), id(0, 14), id(1, 6), id(3, 0))}, {size1: 24, size2: 31, want: rehash(1, 4, id(3, 2), id(0, 30), id(1, 14), id(2, 6), id(4, 0), )}, {size1: 81, size2: 95, want: rehash(4, 7, id(0, 80), id(0, 81), id(1, 41), id(2, 21), id(0, 94), id(1, 46), id(2, 22), id(4, 4), id(6, 0), )}, } { t.Run(fmt.Sprintf("%d:%d", tc.size1, tc.size2), func(t *testing.T) { proof, err := Consistency(tc.size1, tc.size2) if tc.wantErr { if err == nil { t.Fatal("accepted bad params") } return } else if err != nil { t.Fatalf("Consistency: %v", err) } // Ignore the ephemeral node, it is tested separately. proof.ephem = compact.NodeID{} if diff := cmp.Diff(tc.want, proof, cmp.AllowUnexported(Nodes{})); diff != "" { t.Errorf("paths mismatch:\n%v", diff) } }) } } func TestInclusionSucceedsUpToTreeSize(t *testing.T) { const maxSize = uint64(555) for ts := uint64(1); ts <= maxSize; ts++ { for i := ts; i < ts; i++ { if _, err := Inclusion(i, ts); err != nil { t.Errorf("Inclusion(ts:%d, i:%d) = %v", ts, i, err) } } } } func TestConsistencySucceedsUpToTreeSize(t *testing.T) { const maxSize = uint64(100) for s1 := uint64(1); s1 < maxSize; s1++ { for s2 := s1 + 1; s2 <= maxSize; s2++ { if _, err := Consistency(s1, s2); err != nil { t.Errorf("Consistency(%d, %d) = %v", s1, s2, err) } } } } func TestEphem(t *testing.T) { id := compact.NewNodeID for _, tc := range []struct { index uint64 size uint64 want compact.NodeID }{ // Edge case: For perfect trees the ephemeral node is the sibling of the // root. However, it will not be used in the proof, as the corresponding // subtree is empty. {index: 3, size: 32, want: id(5, 1)}, {index: 0, size: 9, want: id(3, 1)}, {index: 0, size: 13, want: id(3, 1)}, {index: 7, size: 13, want: id(3, 1)}, {index: 8, size: 13, want: id(2, 3)}, {index: 11, size: 13, want: id(2, 3)}, // More edge cases when the computed ephemeral node is not used in the // proof, because it is fully outside the tree border. {index: 12, size: 13, want: id(0, 13)}, {index: 13, size: 14, want: id(1, 7)}, // There is only one node (level 0, index 1024) in the right subtree, but // the ephemeral node is at level 10 rather than level 0. This is because // for the purposes of the proof this node is *effectively* at level 10. {index: 123, size: 1025, want: id(10, 1)}, {index: 0, size: 0xFFFF, want: id(15, 1)}, {index: 0xF000, size: 0xFFFF, want: id(11, 0x1F)}, {index: 0xFF00, size: 0xFFFF, want: id(7, 0x1FF)}, {index: 0xFFF0, size: 0xFFFF, want: id(3, 0x1FFF)}, {index: 0xFFFF - 1, size: 0xFFFF, want: id(0, 0xFFFF)}, } { t.Run(fmt.Sprintf("%d:%d", tc.index, tc.size), func(t *testing.T) { nodes, err := Inclusion(tc.index, tc.size) if err != nil { t.Fatalf("Inclusion: %v", err) } got, _, _ := nodes.Ephem() if want := tc.want; got != want { t.Errorf("Ephem: got %+v, want %+v", got, want) } }) } } func TestRehash(t *testing.T) { th := rfc6962.DefaultHasher h := [][]byte{ th.HashLeaf([]byte("Hash 1")), th.HashLeaf([]byte("Hash 2")), th.HashLeaf([]byte("Hash 3")), th.HashLeaf([]byte("Hash 4")), th.HashLeaf([]byte("Hash 5")), } for _, tc := range []struct { desc string hashes [][]byte nodes Nodes want [][]byte }{ { desc: "no-rehash", hashes: h[:3], nodes: inclusion(t, 3, 8), want: h[:3], }, { desc: "rehash", hashes: h[:5], nodes: inclusion(t, 9, 15), want: [][]byte{h[0], h[1], th.HashChildren(h[3], h[2]), h[4]}, }, { desc: "rehash-at-the-end", hashes: h[:4], nodes: inclusion(t, 2, 7), want: [][]byte{h[0], h[1], th.HashChildren(h[3], h[2])}, }, } { t.Run(tc.desc, func(t *testing.T) { h := append([][]byte{}, tc.hashes...) got, err := tc.nodes.Rehash(h, th.HashChildren) if err != nil { t.Fatalf("Rehash: %v", err) } if want := tc.want; !cmp.Equal(got, want) { t.Errorf("proofs mismatch:\ngot: %x\nwant: %x", got, want) } }) } } func inclusion(t *testing.T, index, size uint64) Nodes { t.Helper() n, err := Inclusion(index, size) if err != nil { t.Fatalf("Inclusion: %v", err) } return n } golang-github-transparency-dev-merkle-0.0.2/proof/verify.go000066400000000000000000000143571455123105200237700ustar00rootroot00000000000000// Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package proof import ( "bytes" "errors" "fmt" "math/bits" "github.com/transparency-dev/merkle" ) // RootMismatchError occurs when an inclusion proof fails. type RootMismatchError struct { ExpectedRoot []byte CalculatedRoot []byte } func (e RootMismatchError) Error() string { return fmt.Sprintf("calculated root:\n%v\n does not match expected root:\n%v", e.CalculatedRoot, e.ExpectedRoot) } func verifyMatch(calculated, expected []byte) error { if !bytes.Equal(calculated, expected) { return RootMismatchError{ExpectedRoot: expected, CalculatedRoot: calculated} } return nil } // VerifyInclusion verifies the correctness of the inclusion proof for the leaf // with the specified hash and index, relatively to the tree of the given size // and root hash. Requires 0 <= index < size. func VerifyInclusion(hasher merkle.LogHasher, index, size uint64, leafHash []byte, proof [][]byte, root []byte) error { calcRoot, err := RootFromInclusionProof(hasher, index, size, leafHash, proof) if err != nil { return err } return verifyMatch(calcRoot, root) } // RootFromInclusionProof calculates the expected root hash for a tree of the // given size, provided a leaf index and hash with the corresponding inclusion // proof. Requires 0 <= index < size. func RootFromInclusionProof(hasher merkle.LogHasher, index, size uint64, leafHash []byte, proof [][]byte) ([]byte, error) { if index >= size { return nil, fmt.Errorf("index is beyond size: %d >= %d", index, size) } if got, want := len(leafHash), hasher.Size(); got != want { return nil, fmt.Errorf("leafHash has unexpected size %d, want %d", got, want) } inner, border := decompInclProof(index, size) if got, want := len(proof), inner+border; got != want { return nil, fmt.Errorf("wrong proof size %d, want %d", got, want) } res := chainInner(hasher, leafHash, proof[:inner], index) res = chainBorderRight(hasher, res, proof[inner:]) return res, nil } // VerifyConsistency checks that the passed-in consistency proof is valid // between the passed in tree sizes, with respect to the corresponding root // hashes. Requires 0 <= size1 <= size2. func VerifyConsistency(hasher merkle.LogHasher, size1, size2 uint64, proof [][]byte, root1, root2 []byte) error { switch { case size2 < size1: return fmt.Errorf("size2 (%d) < size1 (%d)", size1, size2) case size1 == size2: if len(proof) > 0 { return errors.New("size1=size2, but proof is not empty") } return verifyMatch(root1, root2) case size1 == 0: // Any size greater than 0 is consistent with size 0. if len(proof) > 0 { return fmt.Errorf("expected empty proof, but got %d components", len(proof)) } return nil // Proof OK. case len(proof) == 0: return errors.New("empty proof") } inner, border := decompInclProof(size1-1, size2) shift := bits.TrailingZeros64(size1) inner -= shift // Note: shift < inner if size1 < size2. // The proof includes the root hash for the sub-tree of size 2^shift. seed, start := proof[0], 1 if size1 == 1<> uint(shift) // Start chaining from level |shift|. hash1 := chainInnerRight(hasher, seed, proof[:inner], mask) hash1 = chainBorderRight(hasher, hash1, proof[inner:]) if err := verifyMatch(hash1, root1); err != nil { return err } // Verify the second root. hash2 := chainInner(hasher, seed, proof[:inner], mask) hash2 = chainBorderRight(hasher, hash2, proof[inner:]) return verifyMatch(hash2, root2) } // decompInclProof breaks down inclusion proof for a leaf at the specified // |index| in a tree of the specified |size| into 2 components. The splitting // point between them is where paths to leaves |index| and |size-1| diverge. // Returns lengths of the bottom and upper proof parts correspondingly. The sum // of the two determines the correct length of the inclusion proof. func decompInclProof(index, size uint64) (int, int) { inner := innerProofSize(index, size) border := bits.OnesCount64(index >> uint(inner)) return inner, border } func innerProofSize(index, size uint64) int { return bits.Len64(index ^ (size - 1)) } // chainInner computes a subtree hash for a node on or below the tree's right // border. Assumes |proof| hashes are ordered from lower levels to upper, and // |seed| is the initial subtree/leaf hash on the path located at the specified // |index| on its level. func chainInner(hasher merkle.LogHasher, seed []byte, proof [][]byte, index uint64) []byte { for i, h := range proof { if (index>>uint(i))&1 == 0 { seed = hasher.HashChildren(seed, h) } else { seed = hasher.HashChildren(h, seed) } } return seed } // chainInnerRight computes a subtree hash like chainInner, but only takes // hashes to the left from the path into consideration, which effectively means // the result is a hash of the corresponding earlier version of this subtree. func chainInnerRight(hasher merkle.LogHasher, seed []byte, proof [][]byte, index uint64) []byte { for i, h := range proof { if (index>>uint(i))&1 == 1 { seed = hasher.HashChildren(h, seed) } } return seed } // chainBorderRight chains proof hashes along tree borders. This differs from // inner chaining because |proof| contains only left-side subtree hashes. func chainBorderRight(hasher merkle.LogHasher, seed []byte, proof [][]byte) []byte { for _, h := range proof { seed = hasher.HashChildren(h, seed) } return seed } golang-github-transparency-dev-merkle-0.0.2/proof/verify_test.go000066400000000000000000000335751455123105200250320ustar00rootroot00000000000000// Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package proof import ( "bytes" "encoding/hex" "fmt" "strings" "testing" "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/rfc6962" ) type inclusionProofTestVector struct { leaf uint64 size uint64 proof [][]byte } type consistencyTestVector struct { size1 uint64 size2 uint64 proof [][]byte } var ( hasher = rfc6962.DefaultHasher sha256SomeHash = dh("abacaba000000000000000000000000000000000000000000060061e00123456", 32) sha256EmptyTreeHash = dh("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 32) inclusionProofs = []inclusionProofTestVector{ {0, 0, nil}, {1, 1, nil}, {1, 8, [][]byte{ dh("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32), dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), dh("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4", 32), }}, {6, 8, [][]byte{ dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32), dh("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", 32), dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32), }}, {3, 3, [][]byte{ dh("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32), }}, {2, 5, [][]byte{ dh("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32), dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32), }}, } consistencyProofs = []consistencyTestVector{ {1, 1, nil}, {1, 8, [][]byte{ dh("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32), dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), dh("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4", 32), }}, {6, 8, [][]byte{ dh("0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a", 32), dh("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", 32), dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32), }}, {2, 5, [][]byte{ dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32), }}, {6, 7, [][]byte{ dh("0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a", 32), dh("b08693ec2e721597130641e8211e7eedccb4c26413963eee6c1e2ed16ffb1a5f", 32), dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32), }}, } roots = [][]byte{ dh("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32), dh("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32), dh("aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77", 32), dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32), dh("4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", 32), dh("76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", 32), dh("ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c", 32), dh("5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328", 32), } leaves = [][]byte{ dh("", 0), dh("00", 1), dh("10", 1), dh("2021", 2), dh("3031", 2), dh("40414243", 4), dh("5051525354555657", 8), dh("606162636465666768696a6b6c6d6e6f", 16), } ) // inclusionProbe is a parameter set for inclusion proof verification. type inclusionProbe struct { leafIndex uint64 treeSize uint64 root []byte leafHash []byte proof [][]byte desc string } // consistencyProbe is a parameter set for consistency proof verification. type consistencyProbe struct { size1 uint64 size2 uint64 root1 []byte root2 []byte proof [][]byte desc string } func corruptInclusionProof(leafIndex, treeSize uint64, proof [][]byte, root, leafHash []byte) []inclusionProbe { ret := []inclusionProbe{ // Wrong leaf index. {leafIndex - 1, treeSize, root, leafHash, proof, "leafIndex - 1"}, {leafIndex + 1, treeSize, root, leafHash, proof, "leafIndex + 1"}, {leafIndex ^ 2, treeSize, root, leafHash, proof, "leafIndex ^ 2"}, // Wrong tree height. {leafIndex, treeSize * 2, root, leafHash, proof, "treeSize * 2"}, {leafIndex, treeSize / 2, root, leafHash, proof, "treeSize / 2"}, // Wrong leaf or root. {leafIndex, treeSize, root, []byte("WrongLeaf"), proof, "wrong leaf"}, {leafIndex, treeSize, sha256EmptyTreeHash, leafHash, proof, "empty root"}, {leafIndex, treeSize, sha256SomeHash, leafHash, proof, "random root"}, // Add garbage at the end. {leafIndex, treeSize, root, leafHash, extend(proof, []byte{}), "trailing garbage"}, {leafIndex, treeSize, root, leafHash, extend(proof, root), "trailing root"}, // Add garbage at the front. {leafIndex, treeSize, root, leafHash, prepend(proof, []byte{}), "preceding garbage"}, {leafIndex, treeSize, root, leafHash, prepend(proof, root), "preceding root"}, } ln := len(proof) // Modify single bit in an element of the proof. for i := 0; i < ln; i++ { wrongProof := prepend(proof) // Copy the proof slice. wrongProof[i] = append([]byte(nil), wrongProof[i]...) // But also the modified data. wrongProof[i][0] ^= 8 // Flip the bit. desc := fmt.Sprintf("modified proof[%d] bit 3", i) ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, wrongProof, desc}) } if ln > 0 { ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, proof[:ln-1], "removed component"}) } if ln > 1 { wrongProof := prepend(proof[1:], proof[0], sha256SomeHash) ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, wrongProof, "inserted component"}) } return ret } func corruptConsistencyProof(size1, size2 uint64, root1, root2 []byte, proof [][]byte) []consistencyProbe { ln := len(proof) ret := []consistencyProbe{ // Wrong size1. {size1 - 1, size2, root1, root2, proof, "size1 - 1"}, {size1 + 1, size2, root1, root2, proof, "size1 + 1"}, {size1 ^ 2, size2, root1, root2, proof, "size1 ^ 2"}, // Wrong tree height. {size1, size2 * 2, root1, root2, proof, "size2 * 2"}, {size1, size2 / 2, root1, root2, proof, "size2 / 2"}, // Wrong root. {size1, size2, []byte("WrongRoot"), root2, proof, "wrong root1"}, {size1, size2, root1, []byte("WrongRoot"), proof, "wrong root2"}, {size1, size2, root2, root1, proof, "swapped roots"}, // Empty proof. {size1, size2, root1, root2, [][]byte{}, "empty proof"}, // Add garbage at the end. {size1, size2, root1, root2, extend(proof, []byte{}), "trailing garbage"}, {size1, size2, root1, root2, extend(proof, root1), "trailing root1"}, {size1, size2, root1, root2, extend(proof, root2), "trailing root2"}, // Add garbage at the front. {size1, size2, root1, root2, prepend(proof, []byte{}), "preceding garbage"}, {size1, size2, root1, root2, prepend(proof, root1), "preceding root1"}, {size1, size2, root1, root2, prepend(proof, root2), "preceding root2"}, {size1, size2, root1, root2, prepend(proof, proof[0]), "preceding proof[0]"}, } // Remove a node from the end. if ln > 0 { ret = append(ret, consistencyProbe{size1, size2, root1, root2, proof[:ln-1], "truncated proof"}) } // Modify single bit in an element of the proof. for i := 0; i < ln; i++ { wrongProof := prepend(proof) // Copy the proof slice. wrongProof[i] = append([]byte(nil), wrongProof[i]...) // But also the modified data. wrongProof[i][0] ^= 16 // Flip the bit. desc := fmt.Sprintf("modified proof[%d] bit 4", i) ret = append(ret, consistencyProbe{size1, size2, root1, root2, wrongProof, desc}) } return ret } func verifierCheck(hasher merkle.LogHasher, leafIndex, treeSize uint64, proof [][]byte, root, leafHash []byte) error { // Verify original inclusion proof. got, err := RootFromInclusionProof(hasher, leafIndex, treeSize, leafHash, proof) if err != nil { return err } if !bytes.Equal(got, root) { return fmt.Errorf("got root:\n%x\nexpected:\n%x", got, root) } if err := VerifyInclusion(hasher, leafIndex, treeSize, leafHash, proof, root); err != nil { return err } probes := corruptInclusionProof(leafIndex, treeSize, proof, root, leafHash) var wrong []string for _, p := range probes { if err := VerifyInclusion(hasher, p.leafIndex, p.treeSize, p.leafHash, p.proof, p.root); err == nil { wrong = append(wrong, p.desc) } } if len(wrong) > 0 { return fmt.Errorf("incorrectly verified against: %s", strings.Join(wrong, ", ")) } return nil } func verifierConsistencyCheck(hasher merkle.LogHasher, size1, size2 uint64, proof [][]byte, root1, root2 []byte) error { // Verify original consistency proof. if err := VerifyConsistency(hasher, size1, size2, proof, root1, root2); err != nil { return err } // For simplicity test only non-trivial proofs that have root1 != root2, // size1 != 0 and size1 != size2. if len(proof) == 0 { return nil } probes := corruptConsistencyProof(size1, size2, root1, root2, proof) var wrong []string for _, p := range probes { if err := VerifyConsistency(hasher, p.size1, p.size2, p.proof, p.root1, p.root2); err == nil { wrong = append(wrong, p.desc) } } if len(wrong) > 0 { return fmt.Errorf("incorrectly verified against: %s", strings.Join(wrong, ", ")) } return nil } func TestVerifyInclusionSingleEntry(t *testing.T) { data := []byte("data") // Root and leaf hash for 1-entry tree are the same. hash := hasher.HashLeaf(data) // The corresponding inclusion proof is empty. proof := [][]byte{} emptyHash := []byte{} for i, tc := range []struct { root []byte leaf []byte wantErr bool }{ {hash, hash, false}, {hash, emptyHash, true}, {emptyHash, hash, true}, {emptyHash, emptyHash, true}, // Wrong hash size. } { t.Run(fmt.Sprintf("test:%d", i), func(t *testing.T) { err := VerifyInclusion(hasher, 0, 1, tc.leaf, proof, tc.root) if got, want := err != nil, tc.wantErr; got != want { t.Errorf("error: %v, want %v", got, want) } }) } } func TestVerifyInclusion(t *testing.T) { proof := [][]byte{} probes := []struct { index, size uint64 }{{0, 0}, {0, 1}, {1, 0}, {2, 1}} for _, p := range probes { t.Run(fmt.Sprintf("probe:%d:%d", p.index, p.size), func(t *testing.T) { if err := VerifyInclusion(hasher, p.index, p.size, sha256SomeHash, proof, []byte{}); err == nil { t.Error("Incorrectly verified invalid root/leaf") } if err := VerifyInclusion(hasher, p.index, p.size, []byte{}, proof, sha256EmptyTreeHash); err == nil { t.Error("Incorrectly verified invalid root/leaf") } if err := VerifyInclusion(hasher, p.index, p.size, sha256SomeHash, proof, sha256EmptyTreeHash); err == nil { t.Error("Incorrectly verified invalid root/leaf") } }) } // i = 0 is an invalid path. for i := 1; i < 6; i++ { p := inclusionProofs[i] t.Run(fmt.Sprintf("proof:%d", i), func(t *testing.T) { leafHash := rfc6962.DefaultHasher.HashLeaf(leaves[p.leaf-1]) if err := verifierCheck(hasher, p.leaf-1, p.size, p.proof, roots[p.size-1], leafHash); err != nil { t.Errorf("verifierCheck(): %s", err) } }) } } func TestVerifyConsistency(t *testing.T) { root1 := []byte("don't care 1") root2 := []byte("don't care 2") proof1 := [][]byte{} proof2 := [][]byte{sha256EmptyTreeHash} tests := []struct { size1, size2 uint64 root1, root2 []byte proof [][]byte wantErr bool }{ {0, 0, root1, root2, proof1, true}, {1, 1, root1, root2, proof1, true}, // Sizes that are always consistent. {0, 0, root1, root1, proof1, false}, {0, 1, root1, root2, proof1, false}, {1, 1, root2, root2, proof1, false}, // Time travel to the past. {1, 0, root1, root2, proof1, true}, {2, 1, root1, root2, proof1, true}, // Empty proof. {1, 2, root1, root2, proof1, true}, // Roots don't match. {0, 0, sha256EmptyTreeHash, root2, proof1, true}, {1, 1, sha256EmptyTreeHash, root2, proof1, true}, // Roots match but the proof is not empty. {0, 0, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true}, {0, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true}, {1, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true}, } for i, p := range tests { t.Run(fmt.Sprintf("test:%d:size:%d-%d", i, p.size1, p.size2), func(t *testing.T) { err := verifierConsistencyCheck(hasher, p.size1, p.size2, p.proof, p.root1, p.root2) if p.wantErr && err == nil { t.Errorf("Incorrectly verified") } else if !p.wantErr && err != nil { t.Errorf("Failed to verify: %v", err) } }) } for i, p := range consistencyProofs { t.Run(fmt.Sprintf("proof:%d", i), func(t *testing.T) { err := verifierConsistencyCheck(hasher, p.size1, p.size2, p.proof, roots[p.size1-1], roots[p.size2-1]) if err != nil { t.Fatalf("Failed to verify known good proof: %s", err) } }) } } // extend explicitly copies |proof| slice and appends |hashes| to it. func extend(proof [][]byte, hashes ...[]byte) [][]byte { res := make([][]byte, len(proof), len(proof)+len(hashes)) copy(res, proof) return append(res, hashes...) } // prepend adds |proof| to the tail of |hashes|. func prepend(proof [][]byte, hashes ...[]byte) [][]byte { return append(hashes, proof...) } func dh(h string, expLen int) []byte { r, err := hex.DecodeString(h) if err != nil { panic(err) } if got := len(r); got != expLen { panic(fmt.Sprintf("decode %q: len=%d, want %d", h, got, expLen)) } return r } golang-github-transparency-dev-merkle-0.0.2/rfc6962/000077500000000000000000000000001455123105200220775ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/rfc6962/rfc6962.go000066400000000000000000000036051455123105200235330ustar00rootroot00000000000000// Copyright 2016 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package rfc6962 provides hashing functionality according to RFC6962. package rfc6962 import ( "crypto" _ "crypto/sha256" // SHA256 is the default algorithm. ) // Domain separation prefixes const ( RFC6962LeafHashPrefix = 0 RFC6962NodeHashPrefix = 1 ) // DefaultHasher is a SHA256 based LogHasher. var DefaultHasher = New(crypto.SHA256) // Hasher implements the RFC6962 tree hashing algorithm. type Hasher struct { crypto.Hash } // New creates a new Hashers.LogHasher on the passed in hash function. func New(h crypto.Hash) *Hasher { return &Hasher{Hash: h} } // EmptyRoot returns a special case for an empty tree. func (t *Hasher) EmptyRoot() []byte { return t.New().Sum(nil) } // HashLeaf returns the Merkle tree leaf hash of the data passed in through leaf. // The data in leaf is prefixed by the LeafHashPrefix. func (t *Hasher) HashLeaf(leaf []byte) []byte { h := t.New() h.Write([]byte{RFC6962LeafHashPrefix}) h.Write(leaf) return h.Sum(nil) } // HashChildren returns the inner Merkle tree node hash of the two child nodes l and r. // The hashed structure is NodeHashPrefix||l||r. func (t *Hasher) HashChildren(l, r []byte) []byte { h := t.New() b := append(append(append( make([]byte, 0, 1+len(l)+len(r)), RFC6962NodeHashPrefix), l...), r...) h.Write(b) return h.Sum(nil) } golang-github-transparency-dev-merkle-0.0.2/rfc6962/rfc6962_test.go000066400000000000000000000062201455123105200245660ustar00rootroot00000000000000// Copyright 2016 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rfc6962 import ( "bytes" "encoding/hex" "testing" ) func TestRFC6962Hasher(t *testing.T) { hasher := DefaultHasher leafHash := hasher.HashLeaf([]byte("L123456")) emptyLeafHash := hasher.HashLeaf([]byte{}) for _, tc := range []struct { desc string got []byte want string }{ // echo -n | sha256sum { desc: "RFC6962 Empty", want: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", got: hasher.EmptyRoot(), }, // Check that the empty hash is not the same as the hash of an empty leaf. // echo -n 00 | xxd -r -p | sha256sum { desc: "RFC6962 Empty Leaf", want: "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", got: emptyLeafHash, }, // echo -n 004C313233343536 | xxd -r -p | sha256sum { desc: "RFC6962 Leaf", want: "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56", got: leafHash, }, // echo -n 014E3132334E343536 | xxd -r -p | sha256sum { desc: "RFC6962 Node", want: "aa217fe888e47007fa15edab33c2b492a722cb106c64667fc2b044444de66bbb", got: hasher.HashChildren([]byte("N123"), []byte("N456")), }, } { t.Run(tc.desc, func(t *testing.T) { wantBytes, err := hex.DecodeString(tc.want) if err != nil { t.Fatalf("hex.DecodeString(%x): %v", tc.want, err) } if got, want := tc.got, wantBytes; !bytes.Equal(got, want) { t.Errorf("got %x, want %x", got, want) } }) } } // TODO(pavelkalinnikov): Apply this test to all LogHasher implementations. func TestRFC6962HasherCollisions(t *testing.T) { hasher := DefaultHasher // Check that different leaves have different hashes. leaf1, leaf2 := []byte("Hello"), []byte("World") hash1 := hasher.HashLeaf(leaf1) hash2 := hasher.HashLeaf(leaf2) if bytes.Equal(hash1, hash2) { t.Errorf("Leaf hashes should differ, but both are %x", hash1) } // Compute an intermediate subtree hash. subHash1 := hasher.HashChildren(hash1, hash2) // Check that this is not the same as a leaf hash of their concatenation. preimage := append(hash1, hash2...) forgedHash := hasher.HashLeaf(preimage) if bytes.Equal(subHash1, forgedHash) { t.Errorf("Hasher is not second-preimage resistant") } // Swap the order of nodes and check that the hash is different. subHash2 := hasher.HashChildren(hash2, hash1) if bytes.Equal(subHash1, subHash2) { t.Errorf("Subtree hash does not depend on the order of leaves") } } func BenchmarkHashChildren(b *testing.B) { h := DefaultHasher l := h.HashLeaf([]byte("one")) r := h.HashLeaf([]byte("or other")) for i := 0; i < b.N; i++ { _ = h.HashChildren(l, r) } } golang-github-transparency-dev-merkle-0.0.2/scripts/000077500000000000000000000000001455123105200224655ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/scripts/check_license.sh000077500000000000000000000015701455123105200256060ustar00rootroot00000000000000#!/bin/bash # # Checks that source files (.go and .proto) have the Apache License header. # Automatically skips generated files. set -eu check_license() { local path="$1" if head -1 "$path" | grep -iq 'generated by'; then return 0 fi # Look for "Apache License" on the file header if ! head -10 "$path" | grep -q 'Apache License'; then # Format: $path:$line:$message echo "$path:10:license header not found" return 1 fi } main() { if [[ $# -lt 1 ]]; then echo "Usage: $0 " exit 1 fi local code=0 while [[ $# -gt 0 ]]; do local path="$1" if [[ -d "$path" ]]; then for f in "$path"/*.{go,proto}; do if [[ ! -f "$f" ]]; then continue # Empty glob fi check_license "$f" || code=1 done else check_license "$path" || code=1 fi shift done exit $code } main "$@" golang-github-transparency-dev-merkle-0.0.2/scripts/presubmit.sh000077500000000000000000000045221455123105200250410ustar00rootroot00000000000000#!/bin/bash # # Presubmit checks for this repository. # # Checks for lint errors, spelling, licensing, correct builds / tests and so on. # Flags may be specified to allow suppressing of checks or automatic fixes, try # `scripts/presubmit.sh --help` for details. # # Globals: # GO_TEST_TIMEOUT: timeout for 'go test'. Optional (defaults to 5m). set -eu check_pkg() { local cmd="$1" local pkg="$2" check_cmd "$cmd" "try running 'go get -u $pkg'" } check_cmd() { local cmd="$1" local msg="$2" if ! type -p "${cmd}" > /dev/null; then echo "${cmd} not found, ${msg}" return 1 fi } usage() { echo "$0 [--coverage] [--fix] [--no-mod-tidy] [--no-build] [--no-linters]" } main() { local coverage=0 local fix=0 local run_mod_tidy=1 local run_build=1 local run_lint=1 while [[ $# -gt 0 ]]; do case "$1" in --coverage) coverage=1 ;; --fix) fix=1 ;; --no-mod-tidy) run_mod_tidy=0 ;; --help) usage exit 0 ;; --no-build) run_build=0 ;; --no-linters) run_lint=0 ;; *) usage exit 1 ;; esac shift 1 done cd "$(dirname "$0")" # at scripts/ cd .. # at top level go_srcs="$(find . -name '*.go' | \ grep -v mock_ | \ grep -v .pb.go | \ grep -v _string.go | \ grep -v .shims.go | \ tr '\n' ' ')" if [[ "$fix" -eq 1 ]]; then check_pkg goimports golang.org/x/tools/cmd/goimports || exit 1 echo 'running gofmt' gofmt -s -w ${go_srcs} echo 'running goimports' goimports -w ${go_srcs} if [[ "$run_mod_tidy" -eq 1 ]]; then echo 'running go mod tidy' go mod tidy fi fi if [[ "${run_build}" -eq 1 ]]; then echo 'running go build' go build ./... export TEST_FLAGS="-timeout=${GO_TEST_TIMEOUT:-5m}" if [[ ${coverage} -eq 1 ]]; then TEST_FLAGS+=" -covermode=atomic -coverprofile=coverage.txt" fi echo "running go test ${TEST_FLAGS} ./..." go test ${TEST_FLAGS} ./... fi if [[ "${run_lint}" -eq 1 ]]; then check_cmd golangci-lint \ 'have you installed github.com/golangci/golangci-lint?' || exit 1 echo 'running golangci-lint' golangci-lint run --deadline=8m echo 'checking license headers' ./scripts/check_license.sh ${go_srcs} fi } main "$@" golang-github-transparency-dev-merkle-0.0.2/testonly/000077500000000000000000000000001455123105200226575ustar00rootroot00000000000000golang-github-transparency-dev-merkle-0.0.2/testonly/constants.go000066400000000000000000000101511455123105200252200ustar00rootroot00000000000000// Copyright 2019 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package testonly contains code and data for testing Merkle trees, such as a // reference implementation of in-memory Merkle tree. package testonly import "encoding/hex" // LeafInputs returns a slice of leaf inputs for testing Merkle trees. func LeafInputs() [][]byte { return [][]byte{ hd(""), hd("00"), hd("10"), hd("2021"), hd("3031"), hd("40414243"), hd("5051525354555657"), hd("606162636465666768696a6b6c6d6e6f"), } } // NodeHashes returns a structured slice of node hashes for all complete // subtrees of a Merkle tree built from LeafInputs() using the RFC 6962 hashing // strategy. The first index in the slice is the tree level (zero being the // leaves level), the second is the horizontal index within a level. func NodeHashes() [][][]byte { return [][][]byte{{ hd("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"), hd("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7"), hd("0298d122906dcfc10892cb53a73992fc5b9f493ea4c9badb27b791b4127a7fe7"), hd("07506a85fd9dd2f120eb694f86011e5bb4662e5c415a62917033d4a9624487e7"), hd("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b"), hd("4271a26be0d8a84f0bd54c8c302e7cb3a3b5d1fa6780a40bcce2873477dab658"), hd("b08693ec2e721597130641e8211e7eedccb4c26413963eee6c1e2ed16ffb1a5f"), hd("46f6ffadd3d06a09ff3c5860d2755c8b9819db7df44251788c7d8e3180de8eb1"), }, { hd("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125"), hd("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e"), hd("0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a"), hd("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0"), }, { hd("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"), hd("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4"), }, { hd("5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328"), }} } // RootHashes returns a slice of Merkle tree root hashes for all subsequent // trees built from LeafInputs() using the RFC 6962 hashing strategy. Hashes // are indexed by tree size starting from an empty tree. func RootHashes() [][]byte { return [][]byte{ EmptyRootHash(), hd("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"), hd("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125"), hd("aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77"), hd("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"), hd("4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4"), hd("76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef"), hd("ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c"), hd("5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328"), } } // CompactTrees returns a slice of compact.Tree internal hashes for all // subsequent trees built from LeafInputs() using the RFC 6962 hashing // strategy. func CompactTrees() [][][]byte { nh := NodeHashes() return [][][]byte{ nil, // Empty tree. {nh[0][0]}, {nh[1][0]}, {nh[1][0], nh[0][2]}, {nh[2][0]}, {nh[2][0], nh[0][4]}, {nh[2][0], nh[1][2]}, {nh[2][0], nh[1][2], nh[0][6]}, {nh[3][0]}, } } // EmptyRootHash returns the root hash for an empty Merkle tree that uses // SHA256-based strategy from RFC 6962. func EmptyRootHash() []byte { return hd("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") } // hd decodes a hex string or panics. func hd(b string) []byte { r, err := hex.DecodeString(b) if err != nil { panic(err) } return r } golang-github-transparency-dev-merkle-0.0.2/testonly/reference_test.go000066400000000000000000000156671455123105200262220ustar00rootroot00000000000000// Copyright 2022 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package testonly import ( "fmt" "math/bits" "testing" "github.com/google/go-cmp/cmp" "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/rfc6962" ) // The reference Merkle tree hashing and proof algorithms in this file directly // implement the definitions from RFC 6962 [1]. We use this implementation only // for testing correctness of other more flexible and performant algorithms, // such as the in-memory Tree type and compact ranges. // // [1] https://datatracker.ietf.org/doc/html/rfc6962#section-2 // refRootHash returns the root hash of a Merkle tree with the given entries. // This is a reference implementation for cross-checking. func refRootHash(entries [][]byte, hasher merkle.LogHasher) []byte { if len(entries) == 0 { return hasher.EmptyRoot() } if len(entries) == 1 { return hasher.HashLeaf(entries[0]) } split := downToPowerOfTwo(uint64(len(entries))) return hasher.HashChildren( refRootHash(entries[:split], hasher), refRootHash(entries[split:], hasher)) } // refInclusionProof returns the inclusion proof for the given leaf index in a // Merkle tree with the given entries. This is a reference implementation for // cross-checking. func refInclusionProof(entries [][]byte, index uint64, hasher merkle.LogHasher) [][]byte { size := uint64(len(entries)) if size == 1 || index >= size { return nil } split := downToPowerOfTwo(size) if index < split { return append( refInclusionProof(entries[:split], index, hasher), refRootHash(entries[split:], hasher)) } return append( refInclusionProof(entries[split:], index-split, hasher), refRootHash(entries[:split], hasher)) } // refConsistencyProof returns the consistency proof for the two tree sizes, in // a Merkle tree with the given entries. This is a reference implementation for // cross-checking. func refConsistencyProof(entries [][]byte, size2, size1 uint64, hasher merkle.LogHasher, haveRoot1 bool) [][]byte { if size1 == 0 || size1 > size2 { return nil } // Consistency proof for two equal sizes is empty. if size1 == size2 { // Record the hash of this subtree if it's not the root for which the proof // was originally requested (which happens when size1 is a power of 2). if !haveRoot1 { return [][]byte{refRootHash(entries[:size1], hasher)} } return nil } // At this point: 0 < size1 < size2. split := downToPowerOfTwo(size2) if size1 <= split { // Root of size1 is in the left subtree of size2. Prove that the left // subtrees are consistent, and record the hash of the right subtree (only // present in size2). return append( refConsistencyProof(entries[:split], split, size1, hasher, haveRoot1), refRootHash(entries[split:], hasher)) } // Root of size1 is at the same level as size2 root. Prove that the right // subtrees are consistent. The right subtree doesn't contain the root of // size1, so set haveRoot1 = false. Record the hash of the left subtree // (equal in both trees). return append( refConsistencyProof(entries[split:], size2-split, size1-split, hasher, false), refRootHash(entries[:split], hasher)) } // downToPowerOfTwo returns the largest power of two smaller than x. func downToPowerOfTwo(x uint64) uint64 { if x < 2 { panic("downToPowerOfTwo requires value >= 2") } return uint64(1) << (bits.Len64(x-1) - 1) } func TestDownToPowerOfTwo(t *testing.T) { for _, inOut := range [][2]uint64{ {2, 1}, {7, 4}, {8, 4}, {63, 32}, {28937, 16384}, } { if got, want := downToPowerOfTwo(inOut[0]), inOut[1]; got != want { t.Errorf("downToPowerOfTwo(%d): got %d, want %d", inOut[0], got, want) } } } func TestRefInclusionProof(t *testing.T) { for _, tc := range []struct { index uint64 size uint64 want [][]byte }{ {index: 0, size: 1, want: nil}, {index: 0, size: 2, want: [][]byte{ hd("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7"), }}, {index: 1, size: 2, want: [][]byte{ hd("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"), }}, {index: 2, size: 3, want: [][]byte{ hd("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125"), }}, {index: 1, size: 5, want: [][]byte{ hd("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"), hd("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e"), hd("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b"), }}, {index: 0, size: 8, want: [][]byte{ hd("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7"), hd("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e"), hd("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4"), }}, {index: 5, size: 8, want: [][]byte{ hd("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b"), hd("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0"), hd("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"), }}, } { t.Run(fmt.Sprintf("%d:%d", tc.index, tc.size), func(t *testing.T) { entries := LeafInputs() got := refInclusionProof(entries[:tc.size], tc.index, rfc6962.DefaultHasher) if diff := cmp.Diff(got, tc.want); diff != "" { t.Errorf("refInclusionProof: diff (-got +want)\n%s", diff) } }) } } func TestRefConsistencyProof(t *testing.T) { for _, tc := range []struct { size1 uint64 size2 uint64 want [][]byte }{ {size1: 1, size2: 1, want: nil}, {size1: 1, size2: 8, want: [][]byte{ hd("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7"), hd("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e"), hd("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4"), }}, {size1: 2, size2: 5, want: [][]byte{ hd("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e"), hd("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b"), }}, {size1: 6, size2: 8, want: [][]byte{ hd("0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a"), hd("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0"), hd("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"), }}, } { t.Run(fmt.Sprintf("%d:%d", tc.size1, tc.size2), func(t *testing.T) { entries := LeafInputs() got := refConsistencyProof(entries[:tc.size2], tc.size2, tc.size1, rfc6962.DefaultHasher, true) if diff := cmp.Diff(got, tc.want); diff != "" { t.Errorf("refConsistencyProof: diff (-got +want)\n%s", diff) } }) } } golang-github-transparency-dev-merkle-0.0.2/testonly/tree.go000066400000000000000000000070011455123105200241430ustar00rootroot00000000000000// Copyright 2022 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package testonly import ( "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/proof" ) // Tree implements an append-only Merkle tree. For testing. type Tree struct { hasher merkle.LogHasher size uint64 hashes [][][]byte // Node hashes, indexed by node (level, index). } // New returns a new empty Merkle tree. func New(hasher merkle.LogHasher) *Tree { return &Tree{hasher: hasher} } // AppendData adds the leaf hashes of the given entries to the end of the tree. func (t *Tree) AppendData(entries ...[]byte) { for _, data := range entries { t.appendImpl(t.hasher.HashLeaf(data)) } } // Append adds the given leaf hashes to the end of the tree. func (t *Tree) Append(hashes ...[]byte) { for _, hash := range hashes { t.appendImpl(hash) } } func (t *Tree) appendImpl(hash []byte) { level := 0 for ; (t.size>>level)&1 == 1; level++ { row := append(t.hashes[level], hash) hash = t.hasher.HashChildren(row[len(row)-2], hash) t.hashes[level] = row } if level > len(t.hashes) { panic("gap in tree appends") } else if level == len(t.hashes) { t.hashes = append(t.hashes, nil) } t.hashes[level] = append(t.hashes[level], hash) t.size++ } // Size returns the current number of leaves in the tree. func (t *Tree) Size() uint64 { return t.size } // LeafHash returns the leaf hash at the given index. // Requires 0 <= index < Size(), otherwise panics. func (t *Tree) LeafHash(index uint64) []byte { return t.hashes[0][index] } // Hash returns the current root hash of the tree. func (t *Tree) Hash() []byte { return t.HashAt(t.size) } // HashAt returns the root hash at the given size. // Requires 0 <= size <= Size(), otherwise panics. func (t *Tree) HashAt(size uint64) []byte { if size == 0 { return t.hasher.EmptyRoot() } hashes := t.getNodes(compact.RangeNodes(0, size, nil)) hash := hashes[len(hashes)-1] for i := len(hashes) - 2; i >= 0; i-- { hash = t.hasher.HashChildren(hashes[i], hash) } return hash } // InclusionProof returns the inclusion proof for the given leaf index in the // tree of the given size. Requires 0 <= index < size <= Size(), otherwise may // panic. func (t *Tree) InclusionProof(index, size uint64) ([][]byte, error) { nodes, err := proof.Inclusion(index, size) if err != nil { return nil, err } return nodes.Rehash(t.getNodes(nodes.IDs), t.hasher.HashChildren) } // ConsistencyProof returns the consistency proof between the two given tree // sizes. Requires 0 <= size1 <= size2 <= Size(), otherwise may panic. func (t *Tree) ConsistencyProof(size1, size2 uint64) ([][]byte, error) { nodes, err := proof.Consistency(size1, size2) if err != nil { return nil, err } return nodes.Rehash(t.getNodes(nodes.IDs), t.hasher.HashChildren) } func (t *Tree) getNodes(ids []compact.NodeID) [][]byte { hashes := make([][]byte, len(ids)) for i, id := range ids { hashes[i] = t.hashes[id.Level][id.Index] } return hashes } golang-github-transparency-dev-merkle-0.0.2/testonly/tree_fuzz_test.go000066400000000000000000000074251455123105200262720ustar00rootroot00000000000000//go:build go1.18 package testonly import ( "bytes" "math" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/transparency-dev/merkle/proof" ) // Compute and verify consistency proofs func FuzzConsistencyProofAndVerify(f *testing.F) { for size := 0; size <= 8; size++ { for end := 0; end <= size; end++ { for begin := 0; begin <= end; begin++ { f.Add(uint64(size), uint64(begin), uint64(end)) } } } f.Fuzz(func(t *testing.T, size, begin, end uint64) { // necessary to restrict size for compile_native_go_fuzzer if size >= math.MaxUint16 { return } t.Logf("size=%d, begin=%d, end=%d", size, begin, end) if begin > end || end > size { return } tree := newTree(genEntries(size)) p, err := tree.ConsistencyProof(begin, end) t.Logf("proof=%v", p) if err != nil { t.Error(err) } err = proof.VerifyConsistency(tree.hasher, begin, end, p, tree.HashAt(begin), tree.HashAt(end)) if err != nil { t.Error(err) } }) } // Compute and verify inclusion proofs func FuzzInclusionProofAndVerify(f *testing.F) { for size := 0; size <= 8; size++ { for index := 0; index <= size; index++ { f.Add(uint64(index), uint64(size)) } } f.Fuzz(func(t *testing.T, index, size uint64) { if size >= math.MaxUint16 { return } t.Logf("index=%d, size=%d", index, size) if index >= size { return } tree := newTree(genEntries(size)) p, err := tree.InclusionProof(index, size) t.Logf("proof=%v", p) if err != nil { t.Error(err) } err = proof.VerifyInclusion(tree.hasher, index, size, tree.LeafHash(index), p, tree.Hash()) if err != nil { t.Error(err) } }) } func FuzzHashAtAgainstReferenceImplementation(f *testing.F) { for size := 0; size <= 8; size++ { for index := 0; index <= size; index++ { f.Add(uint64(index), uint64(size)) } } f.Fuzz(func(t *testing.T, index, size uint64) { if size >= math.MaxUint16 { return } t.Logf("index=%d, size=%d", index, size) if index >= size { return } entries := genEntries(size) mt := newTree(entries) got := mt.HashAt(uint64(size)) want := refRootHash(entries[:size], mt.hasher) if !bytes.Equal(got, want) { t.Errorf("HashAt(%d): %x, want %x", size, got, want) } }) } func FuzzInclusionProofAgainstReferenceImplementation(f *testing.F) { for size := 0; size <= 8; size++ { for index := 0; index <= size; index++ { f.Add(uint64(index), uint64(size)) } } f.Fuzz(func(t *testing.T, index, size uint64) { if size >= math.MaxUint16 { return } t.Logf("index=%d, size=%d", index, size) if index >= size { return } entries := genEntries(size) tree := newTree(entries) got, err := tree.InclusionProof(index, size) t.Logf("proof=%v", got) if err != nil { t.Error(err) } want := refInclusionProof(entries, index, tree.hasher) if diff := cmp.Diff(got, want, cmpopts.EquateEmpty()); diff != "" { t.Errorf("InclusionProof(%d, %d): diff (-got +want)\n%s", index, size, diff) } }) } func FuzzConsistencyProofAgainstReferenceImplementation(f *testing.F) { for size := 0; size <= 8; size++ { for end := 0; end <= size; end++ { for begin := 0; begin <= end; begin++ { f.Add(uint64(size), uint64(begin), uint64(end)) } } } f.Fuzz(func(t *testing.T, size, begin, end uint64) { if size >= math.MaxUint16 { return } t.Logf("size=%d, begin=%d, end=%d", size, begin, end) if begin > end || end > size { return } entries := genEntries(size) tree := newTree(entries) got, err := tree.ConsistencyProof(begin, end) if err != nil { t.Errorf("ConsistencyProof: %v", err) } want := refConsistencyProof(entries[:end], end, begin, tree.hasher, true) if diff := cmp.Diff(got, want, cmpopts.EquateEmpty()); diff != "" { t.Errorf("ConsistencyProof: diff (-got +want)\n%s", diff) } }) } golang-github-transparency-dev-merkle-0.0.2/testonly/tree_test.go000066400000000000000000000130651455123105200252110ustar00rootroot00000000000000// Copyright 2022 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package testonly import ( "bytes" "fmt" "math/rand" "strconv" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/transparency-dev/merkle/rfc6962" ) func validateTree(t *testing.T, mt *Tree, size uint64) { t.Helper() if got, want := mt.Size(), size; got != want { t.Errorf("Size: %d, want %d", got, want) } roots := RootHashes() if got, want := mt.Hash(), roots[size]; !bytes.Equal(got, want) { t.Errorf("Hash(%d): %x, want %x", size, got, want) } for s := uint64(0); s <= size; s++ { if got, want := mt.HashAt(s), roots[s]; !bytes.Equal(got, want) { t.Errorf("HashAt(%d/%d): %x, want %x", s, size, got, want) } } } func TestBuildTreeBuildOneAtATime(t *testing.T) { mt := newTree(nil) validateTree(t, mt, 0) for i, entry := range LeafInputs() { mt.AppendData(entry) validateTree(t, mt, uint64(i+1)) } } func TestBuildTreeBuildTwoChunks(t *testing.T) { entries := LeafInputs() mt := newTree(nil) mt.AppendData(entries[:3]...) validateTree(t, mt, 3) mt.AppendData(entries[3:8]...) validateTree(t, mt, 8) } func TestBuildTreeBuildAllAtOnce(t *testing.T) { mt := newTree(nil) mt.AppendData(LeafInputs()...) validateTree(t, mt, 8) } func TestTreeHashAt(t *testing.T) { test := func(desc string, entries [][]byte) { t.Run(desc, func(t *testing.T) { mt := newTree(entries) for size := 0; size <= len(entries); size++ { got := mt.HashAt(uint64(size)) want := refRootHash(entries[:size], mt.hasher) if !bytes.Equal(got, want) { t.Errorf("HashAt(%d): %x, want %x", size, got, want) } } }) } entries := LeafInputs() for size := 0; size <= len(entries); size++ { test(fmt.Sprintf("size:%d", size), entries[:size]) } test("generated", genEntries(256)) } func TestTreeInclusionProof(t *testing.T) { test := func(desc string, entries [][]byte) { t.Run(desc, func(t *testing.T) { mt := newTree(entries) for index, size := uint64(0), uint64(len(entries)); index < size; index++ { got, err := mt.InclusionProof(index, size) if err != nil { t.Fatalf("InclusionProof(%d, %d): %v", index, size, err) } want := refInclusionProof(entries[:size], index, mt.hasher) if diff := cmp.Diff(got, want, cmpopts.EquateEmpty()); diff != "" { t.Fatalf("InclusionProof(%d, %d): diff (-got +want)\n%s", index, size, diff) } } }) } test("generated", genEntries(256)) entries := LeafInputs() for size := 0; size < len(entries); size++ { test(fmt.Sprintf("golden:%d", size), entries[:size]) } } func TestTreeConsistencyProof(t *testing.T) { entries := LeafInputs() mt := newTree(entries) validateTree(t, mt, 8) if _, err := mt.ConsistencyProof(6, 3); err == nil { t.Error("ConsistencyProof(6, 3) succeeded unexpectedly") } for size1 := uint64(0); size1 <= 8; size1++ { for size2 := size1; size2 <= 8; size2++ { t.Run(fmt.Sprintf("%d:%d", size1, size2), func(t *testing.T) { got, err := mt.ConsistencyProof(size1, size2) if err != nil { t.Fatalf("ConsistencyProof: %v", err) } want := refConsistencyProof(entries[:size2], size2, size1, mt.hasher, true) if diff := cmp.Diff(got, want, cmpopts.EquateEmpty()); diff != "" { t.Errorf("ConsistencyProof: diff (-got +want)\n%s", diff) } }) } } } // Make random proof queries and check against the reference implementation. func TestTreeConsistencyProofFuzz(t *testing.T) { entries := genEntries(256) for treeSize := int64(1); treeSize <= 256; treeSize++ { mt := newTree(entries[:treeSize]) for i := 0; i < 8; i++ { size2 := uint64(rand.Int63n(treeSize + 1)) size1 := uint64(rand.Int63n(int64(size2) + 1)) got, err := mt.ConsistencyProof(size1, size2) if err != nil { t.Fatalf("ConsistencyProof: %v", err) } want := refConsistencyProof(entries[:size2], size2, size1, mt.hasher, true) if diff := cmp.Diff(got, want, cmpopts.EquateEmpty()); diff != "" { t.Errorf("ConsistencyProof: diff (-got +want)\n%s", diff) } } } } func TestTreeAppend(t *testing.T) { entries := genEntries(256) mt1 := newTree(entries) mt2 := newTree(nil) for _, entry := range entries { mt2.Append(rfc6962.DefaultHasher.HashLeaf(entry)) } if diff := cmp.Diff(mt1, mt2, cmp.AllowUnexported(Tree{})); diff != "" { t.Errorf("Trees built with AppendData and Append mismatch: diff (-mt1 +mt2)\n%s", diff) } } func TestTreeAppendAssociativity(t *testing.T) { entries := genEntries(256) mt1 := newTree(nil) mt1.AppendData(entries...) mt2 := newTree(nil) for _, entry := range entries { mt2.AppendData(entry) } if diff := cmp.Diff(mt1, mt2, cmp.AllowUnexported(Tree{})); diff != "" { t.Errorf("AppendData is not associative: diff (-mt1 +mt2)\n%s", diff) } } func newTree(entries [][]byte) *Tree { tree := New(rfc6962.DefaultHasher) tree.AppendData(entries...) return tree } // genEntries a slice of entries of the given size. func genEntries(size uint64) [][]byte { entries := make([][]byte, size) for i := range entries { entries[i] = []byte(strconv.Itoa(i)) } return entries }